From 5fe6bb961d81055fdbfe34e80bec29411f1c691e Mon Sep 17 00:00:00 2001 From: jvjctk Date: Wed, 16 Jun 2021 13:13:26 +0200 Subject: [PATCH] object_detection --- .../object_detection/CONTRIBUTING.md | 13 + .../virtuallab/object_detection/README.md | 191 + .../virtuallab/object_detection/__init__.py | 0 .../virtuallab/object_detection/__init__.pyc | Bin 0 -> 132 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 114 bytes .../__pycache__/eval_util.cpython-36.pyc | Bin 0 -> 38847 bytes .../__pycache__/model_lib.cpython-36.pyc | Bin 0 -> 29863 bytes .../anchor_generators/__init__.py | 0 .../anchor_generators/__init__.pyc | Bin 0 -> 150 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 155 bytes ...xible_grid_anchor_generator.cpython-36.pyc | Bin 0 -> 6097 bytes .../grid_anchor_generator.cpython-36.pyc | Bin 0 -> 7754 bytes ...tiple_grid_anchor_generator.cpython-36.pyc | Bin 0 -> 14376 bytes ...scale_grid_anchor_generator.cpython-36.pyc | Bin 0 -> 5837 bytes .../flexible_grid_anchor_generator.py | 134 + .../flexible_grid_anchor_generator.pyc | Bin 0 -> 6569 bytes .../flexible_grid_anchor_generator_test.py | 292 + .../grid_anchor_generator.py | 213 + .../grid_anchor_generator.pyc | Bin 0 -> 8626 bytes .../grid_anchor_generator_test.py | 104 + .../multiple_grid_anchor_generator.py | 342 + .../multiple_grid_anchor_generator.pyc | Bin 0 -> 14295 bytes .../multiple_grid_anchor_generator_test.py | 289 + .../multiscale_grid_anchor_generator.py | 152 + .../multiscale_grid_anchor_generator.pyc | Bin 0 -> 6362 bytes .../multiscale_grid_anchor_generator_test.py | 308 + .../object_detection/box_coders/__init__.py | 0 .../object_detection/box_coders/__init__.pyc | Bin 0 -> 143 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 148 bytes .../faster_rcnn_box_coder.cpython-36.pyc | Bin 0 -> 3276 bytes .../keypoint_box_coder.cpython-36.pyc | Bin 0 -> 5040 bytes .../mean_stddev_box_coder.cpython-36.pyc | Bin 0 -> 2386 bytes .../square_box_coder.cpython-36.pyc | Bin 0 -> 3805 bytes .../box_coders/faster_rcnn_box_coder.py | 118 + .../box_coders/faster_rcnn_box_coder.pyc | Bin 0 -> 3866 bytes .../box_coders/faster_rcnn_box_coder_test.py | 113 + .../box_coders/keypoint_box_coder.py | 173 + .../box_coders/keypoint_box_coder.pyc | Bin 0 -> 5876 bytes .../box_coders/keypoint_box_coder_test.py | 151 + .../box_coders/mean_stddev_box_coder.py | 79 + .../box_coders/mean_stddev_box_coder.pyc | Bin 0 -> 2770 bytes .../box_coders/mean_stddev_box_coder_test.py | 61 + .../box_coders/square_box_coder.py | 126 + .../box_coders/square_box_coder.pyc | Bin 0 -> 4434 bytes .../box_coders/square_box_coder_test.py | 114 + .../object_detection/builders/__init__.py | 0 .../object_detection/builders/__init__.pyc | Bin 0 -> 141 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 146 bytes .../anchor_generator_builder.cpython-36.pyc | Bin 0 -> 3146 bytes .../box_coder_builder.cpython-36.pyc | Bin 0 -> 1497 bytes .../box_predictor_builder.cpython-36.pyc | Bin 0 -> 26507 bytes .../calibration_builder.cpython-36.pyc | Bin 0 -> 7897 bytes .../dataset_builder.cpython-36.pyc | Bin 0 -> 7931 bytes .../decoder_builder.cpython-36.pyc | Bin 0 -> 1858 bytes .../graph_rewriter_builder.cpython-36.pyc | Bin 0 -> 1330 bytes .../hyperparams_builder.cpython-36.pyc | Bin 0 -> 12590 bytes .../image_resizer_builder.cpython-36.pyc | Bin 0 -> 4870 bytes .../__pycache__/losses_builder.cpython-36.pyc | Bin 0 -> 6128 bytes .../matcher_builder.cpython-36.pyc | Bin 0 -> 1458 bytes .../__pycache__/model_builder.cpython-36.pyc | Bin 0 -> 28719 bytes .../optimizer_builder.cpython-36.pyc | Bin 0 -> 4701 bytes .../post_processing_builder.cpython-36.pyc | Bin 0 -> 6705 bytes .../preprocessor_builder.cpython-36.pyc | Bin 0 -> 10904 bytes ...milarity_calculator_builder.cpython-36.pyc | Bin 0 -> 1561 bytes .../builders/anchor_generator_builder.py | 116 + .../builders/anchor_generator_builder.pyc | Bin 0 -> 3342 bytes .../builders/anchor_generator_builder_test.py | 339 + .../builders/box_coder_builder.py | 66 + .../builders/box_coder_builder.pyc | Bin 0 -> 1781 bytes .../builders/box_coder_builder_test.py | 136 + .../builders/box_predictor_builder.py | 975 ++ .../builders/box_predictor_builder.pyc | Bin 0 -> 28727 bytes .../builders/box_predictor_builder_test.py | 667 + .../builders/calibration_builder.py | 250 + .../builders/calibration_builder.pyc | Bin 0 -> 8702 bytes .../builders/calibration_builder_test.py | 233 + .../builders/dataset_builder.py | 250 + .../builders/dataset_builder.pyc | Bin 0 -> 9095 bytes .../builders/dataset_builder_test.py | 716 + .../builders/decoder_builder.py | 72 + .../builders/decoder_builder.pyc | Bin 0 -> 2231 bytes .../builders/decoder_builder_test.py | 193 + .../builders/graph_rewriter_builder.py | 53 + .../builders/graph_rewriter_builder.pyc | Bin 0 -> 1594 bytes .../graph_rewriter_builder_tf1_test.py | 67 + .../builders/hyperparams_builder.py | 434 + .../builders/hyperparams_builder.pyc | Bin 0 -> 14502 bytes .../builders/hyperparams_builder_test.py | 977 ++ .../builders/image_resizer_builder.py | 187 + .../builders/image_resizer_builder.pyc | Bin 0 -> 5579 bytes .../builders/image_resizer_builder_test.py | 243 + .../builders/input_reader_builder.py | 91 + .../builders/input_reader_builder_tf1_test.py | 306 + .../builders/losses_builder.py | 260 + .../builders/losses_builder.pyc | Bin 0 -> 7235 bytes .../builders/losses_builder_test.py | 558 + .../builders/matcher_builder.py | 58 + .../builders/matcher_builder.pyc | Bin 0 -> 1741 bytes .../builders/matcher_builder_test.py | 105 + .../builders/model_builder.py | 1090 ++ .../builders/model_builder.pyc | Bin 0 -> 33813 bytes .../builders/model_builder_test.py | 356 + .../builders/model_builder_tf1_test.py | 58 + .../builders/model_builder_tf2_test.py | 303 + .../builders/optimizer_builder.py | 205 + .../builders/optimizer_builder.pyc | Bin 0 -> 5419 bytes .../builders/optimizer_builder_tf1_test.py | 224 + .../builders/optimizer_builder_tf2_test.py | 104 + .../builders/post_processing_builder.py | 183 + .../builders/post_processing_builder.pyc | Bin 0 -> 7600 bytes .../builders/post_processing_builder_test.py | 185 + .../builders/preprocessor_builder.py | 428 + .../builders/preprocessor_builder.pyc | Bin 0 -> 9897 bytes .../builders/preprocessor_builder_test.py | 758 + .../region_similarity_calculator_builder.py | 59 + .../region_similarity_calculator_builder.pyc | Bin 0 -> 1782 bytes ...gion_similarity_calculator_builder_test.py | 67 + .../builders/target_assigner_builder.py | 40 + .../builders/target_assigner_builder_test.py | 50 + ...object_detection_tutorial-checkpoint.ipynb | 783 + .../context_rcnn_tutorial.ipynb | 1500 ++ ...eager_few_shot_od_training_tf2_colab.ipynb | 685 + .../eager_few_shot_od_training_tflite.ipynb | 730 + ...inference_from_saved_model_tf2_colab.ipynb | 313 + .../colab_tutorials/inference_tf2_colab.ipynb | 470 + .../object_detection_tutorial.ipynb | 847 + ...ourglass104_1024x1024_coco17_tpu-32.config | 129 + ...ass104_1024x1024_kpts_coco17_tpu-32.config | 374 + ...t_hourglass104_512x512_coco17_tpu-8.config | 143 + ...glass104_512x512_kpts_coco17_tpu-32.config | 395 + ...snet101_v1_fpn_512x512_coco17_tpu-8.config | 141 + ...50_v1_fpn_512x512_kpts_coco17_tpu-8.config | 392 + ...snet50_v2_512x512_kpts_coco17_tpu-8.config | 393 + ...resnet101_v1_1024x1024_coco17_tpu-8.config | 166 + ...n_resnet101_v1_640x640_coco17_tpu-8.config | 145 + ..._resnet101_v1_800x1333_coco17_gpu-8.config | 154 + ...resnet152_v1_1024x1024_coco17_tpu-8.config | 166 + ...n_resnet152_v1_640x640_coco17_tpu-8.config | 145 + ..._resnet152_v1_800x1333_coco17_gpu-8.config | 154 + ..._resnet50_v1_1024x1024_coco17_tpu-8.config | 166 + ...nn_resnet50_v1_640x640_coco17_tpu-8.config | 145 + ...n_resnet50_v1_800x1333_coco17_gpu-8.config | 154 + ...esnet50_v1_fpn_640x640_coco17_tpu-8.config | 173 + ...on_resnet_v2_1024x1024_coco17_gpu-8.config | 160 + ...fficientdet_d0_512x512_coco17_tpu-8.config | 199 + ...fficientdet_d1_640x640_coco17_tpu-8.config | 199 + ...fficientdet_d2_768x768_coco17_tpu-8.config | 199 + ...ficientdet_d3_896x896_coco17_tpu-32.config | 199 + ...cientdet_d4_1024x1024_coco17_tpu-32.config | 199 + ...cientdet_d5_1280x1280_coco17_tpu-32.config | 199 + ...cientdet_d6_1408x1408_coco17_tpu-32.config | 201 + ...cientdet_d7_1536x1536_coco17_tpu-32.config | 201 + ...bilenet_v1_fpn_640x640_coco17_tpu-8.config | 197 + ...d_mobilenet_v2_320x320_coco17_tpu-8.config | 197 + ...net_v2_fpnlite_320x320_coco17_tpu-8.config | 201 + ...net_v2_fpnlite_640x640_coco17_tpu-8.config | 201 + ...et101_v1_fpn_1024x1024_coco17_tpu-8.config | 197 + ...snet101_v1_fpn_640x640_coco17_tpu-8.config | 197 + ...et152_v1_fpn_1024x1024_coco17_tpu-8.config | 197 + ...snet152_v1_fpn_640x640_coco17_tpu-8.config | 197 + ...net50_v1_fpn_1024x1024_coco17_tpu-8.config | 197 + ...esnet50_v1_fpn_640x640_coco17_tpu-8.config | 197 + .../object_detection/core/__init__.py | 1 + .../object_detection/core/__init__.pyc | Bin 0 -> 137 bytes .../core/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 142 bytes .../anchor_generator.cpython-36.pyc | Bin 0 -> 6112 bytes ...d_positive_negative_sampler.cpython-36.pyc | Bin 0 -> 8398 bytes .../core/__pycache__/batcher.cpython-36.pyc | Bin 0 -> 4769 bytes .../core/__pycache__/box_coder.cpython-36.pyc | Bin 0 -> 5079 bytes .../core/__pycache__/box_list.cpython-36.pyc | Bin 0 -> 7322 bytes .../__pycache__/box_list_ops.cpython-36.pyc | Bin 0 -> 40564 bytes .../__pycache__/box_predictor.cpython-36.pyc | Bin 0 -> 9991 bytes .../__pycache__/data_decoder.cpython-36.pyc | Bin 0 -> 1161 bytes .../__pycache__/densepose_ops.cpython-36.pyc | Bin 0 -> 13658 bytes .../freezable_batch_norm.cpython-36.pyc | Bin 0 -> 2635 bytes .../__pycache__/keypoint_ops.cpython-36.pyc | Bin 0 -> 14050 bytes .../core/__pycache__/losses.cpython-36.pyc | Bin 0 -> 32099 bytes .../core/__pycache__/matcher.cpython-36.pyc | Bin 0 -> 10610 bytes .../minibatch_sampler.cpython-36.pyc | Bin 0 -> 2990 bytes .../core/__pycache__/model.cpython-36.pyc | Bin 0 -> 22185 bytes .../post_processing.cpython-36.pyc | Bin 0 -> 39874 bytes .../__pycache__/prefetcher.cpython-36.pyc | Bin 0 -> 2045 bytes .../__pycache__/preprocessor.cpython-36.pyc | Bin 0 -> 152375 bytes .../preprocessor_cache.cpython-36.pyc | Bin 0 -> 3657 bytes ...egion_similarity_calculator.cpython-36.pyc | Bin 0 -> 6970 bytes .../standard_fields.cpython-36.pyc | Bin 0 -> 14155 bytes .../target_assigner.cpython-36.pyc | Bin 0 -> 77744 bytes .../object_detection/core/anchor_generator.py | 171 + .../core/anchor_generator.pyc | Bin 0 -> 6684 bytes .../balanced_positive_negative_sampler.py | 262 + .../balanced_positive_negative_sampler.pyc | Bin 0 -> 9511 bytes ...balanced_positive_negative_sampler_test.py | 212 + .../core/batch_multiclass_nms_test.py | 686 + .../object_detection/core/batcher.py | 141 + .../object_detection/core/batcher.pyc | Bin 0 -> 5358 bytes .../object_detection/core/batcher_tf1_test.py | 165 + .../object_detection/core/box_coder.py | 158 + .../object_detection/core/box_coder.pyc | Bin 0 -> 5526 bytes .../object_detection/core/box_coder_test.py | 62 + .../object_detection/core/box_list.py | 210 + .../object_detection/core/box_list.pyc | Bin 0 -> 8181 bytes .../object_detection/core/box_list_ops.py | 1213 ++ .../object_detection/core/box_list_ops.pyc | Bin 0 -> 46226 bytes .../core/box_list_ops_test.py | 1104 ++ .../object_detection/core/box_list_test.py | 121 + .../object_detection/core/box_predictor.py | 227 + .../object_detection/core/box_predictor.pyc | Bin 0 -> 10672 bytes .../core/class_agnostic_nms_test.py | 144 + .../object_detection/core/data_decoder.py | 44 + .../object_detection/core/data_decoder.pyc | Bin 0 -> 1349 bytes .../object_detection/core/data_parser.py | 45 + .../object_detection/core/densepose_ops.py | 380 + .../object_detection/core/densepose_ops.pyc | Bin 0 -> 15394 bytes .../core/densepose_ops_test.py | 178 + .../core/freezable_batch_norm.py | 68 + .../core/freezable_batch_norm.pyc | Bin 0 -> 2834 bytes .../core/freezable_batch_norm_tf2_test.py | 198 + .../object_detection/core/keypoint_ops.py | 376 + .../object_detection/core/keypoint_ops.pyc | Bin 0 -> 15651 bytes .../core/keypoint_ops_test.py | 365 + .../object_detection/core/losses.py | 808 + .../object_detection/core/losses.pyc | Bin 0 -> 34465 bytes .../object_detection/core/losses_test.py | 1451 ++ .../object_detection/core/matcher.py | 270 + .../object_detection/core/matcher.pyc | Bin 0 -> 11770 bytes .../object_detection/core/matcher_test.py | 191 + .../core/minibatch_sampler.py | 94 + .../core/minibatch_sampler.pyc | Bin 0 -> 3372 bytes .../core/minibatch_sampler_test.py | 71 + .../virtuallab/object_detection/core/model.py | 550 + .../object_detection/core/model.pyc | Bin 0 -> 23175 bytes .../object_detection/core/model_test.py | 101 + .../core/multiclass_nms_test.py | 583 + .../object_detection/core/post_processing.py | 1241 ++ .../object_detection/core/post_processing.pyc | Bin 0 -> 43919 bytes .../object_detection/core/prefetcher.py | 61 + .../object_detection/core/prefetcher.pyc | Bin 0 -> 2080 bytes .../core/prefetcher_tf1_test.py | 109 + .../object_detection/core/preprocessor.py | 4607 +++++ .../object_detection/core/preprocessor.pyc | Bin 0 -> 170622 bytes .../core/preprocessor_cache.py | 109 + .../core/preprocessor_cache.pyc | Bin 0 -> 4242 bytes .../core/preprocessor_test.py | 3962 +++++ .../core/region_similarity_calculator.py | 193 + .../core/region_similarity_calculator.pyc | Bin 0 -> 8113 bytes .../core/region_similarity_calculator_test.py | 117 + .../object_detection/core/standard_fields.py | 338 + .../object_detection/core/standard_fields.pyc | Bin 0 -> 15159 bytes .../object_detection/core/target_assigner.py | 2284 +++ .../object_detection/core/target_assigner.pyc | Bin 0 -> 84528 bytes .../core/target_assigner_test.py | 2505 +++ .../data/ava_label_map_v2.1.pbtxt | 240 + .../data/face_label_map.pbtxt | 6 + ...face_person_with_keypoints_label_map.pbtxt | 102 + .../data/fgvc_2854_classes_label_map.pbtxt | 14270 ++++++++++++++++ .../data/kitti_label_map.pbtxt | 9 + .../data/mscoco_complete_label_map.pbtxt | 455 + .../data/mscoco_label_map.pbtxt | 400 + .../data/mscoco_minival_ids.txt | 8059 +++++++++ .../data/oid_bbox_trainable_label_map.pbtxt | 2725 +++ ...ct_detection_challenge_500_label_map.pbtxt | 2500 +++ .../data/oid_v4_label_map.pbtxt | 3005 ++++ .../data/pascal_label_map.pbtxt | 99 + .../object_detection/data/pet_label_map.pbtxt | 184 + .../data/snapshot_serengeti_label_map.pbtxt | 240 + .../object_detection/data/test_labels.csv | 121 + .../object_detection/data/train_labels.csv | 577 + .../data_decoders/__init__.py | 0 .../data_decoders/__init__.pyc | Bin 0 -> 146 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 142 bytes .../tf_example_decoder.cpython-36.pyc | Bin 0 -> 27655 bytes ...tf_sequence_example_decoder.cpython-36.pyc | Bin 0 -> 10454 bytes .../data_decoders/tf_example_decoder.py | 881 + .../data_decoders/tf_example_decoder.pyc | Bin 0 -> 31337 bytes .../data_decoders/tf_example_decoder_test.py | 1532 ++ .../tf_sequence_example_decoder.py | 314 + .../tf_sequence_example_decoder.pyc | Bin 0 -> 11811 bytes .../tf_sequence_example_decoder_test.py | 173 + .../dataset_tools/__init__.py | 0 .../dataset_tools/context_rcnn/__init__.py | 0 .../context_rcnn/add_context_to_examples.py | 938 + .../add_context_to_examples_tf2_test.py | 396 + .../create_cococameratraps_tfexample_main.py | 334 + ...eate_cococameratraps_tfexample_tf2_test.py | 214 + .../context_rcnn/generate_detection_data.py | 283 + .../generate_detection_data_tf2_test.py | 261 + .../context_rcnn/generate_embedding_data.py | 355 + .../generate_embedding_data_tf2_test.py | 330 + .../create_ava_actions_tf_record.py | 540 + .../dataset_tools/create_coco_tf_record.py | 518 + .../create_coco_tf_record_test.py | 497 + .../dataset_tools/create_kitti_tf_record.py | 310 + .../create_kitti_tf_record_test.py | 132 + .../dataset_tools/create_oid_tf_record.py | 117 + .../dataset_tools/create_pascal_tf_record.py | 185 + .../create_pascal_tf_record_test.py | 121 + .../dataset_tools/create_pet_tf_record.py | 318 + .../create_pycocotools_package.sh | 53 + .../dataset_tools/create_tf_record.py | 106 + .../densepose/UV_symmetry_transforms.mat | Bin 0 -> 3148672 bytes .../download_and_preprocess_ava.sh | 30 + .../download_and_preprocess_mscoco.sh | 106 + .../oid_hierarchical_labels_expansion.py | 233 + .../oid_hierarchical_labels_expansion_test.py | 116 + .../dataset_tools/oid_tfrecord_creation.py | 112 + .../oid_tfrecord_creation_test.py | 200 + .../dataset_tools/seq_example_util.py | 282 + .../dataset_tools/seq_example_util_test.py | 366 + .../dataset_tools/tf_record_creation_util.py | 48 + .../tf_record_creation_util_test.py | 49 + .../dockerfiles/android/Dockerfile | 140 + .../dockerfiles/android/README.md | 69 + .../dockerfiles/tf1/Dockerfile | 41 + .../dockerfiles/tf1/README.md | 11 + .../dockerfiles/tf2/Dockerfile | 41 + .../dockerfiles/tf2/README.md | 11 + .../virtuallab/object_detection/eval_util.py | 1206 ++ .../object_detection/eval_util_test.py | 461 + .../export_inference_graph.py | 206 + .../export_tflite_graph_lib_tf2.py | 254 + .../export_tflite_graph_lib_tf2_test.py | 245 + .../export_tflite_graph_tf2.py | 126 + .../export_tflite_ssd_graph.py | 144 + .../export_tflite_ssd_graph_lib.py | 334 + .../export_tflite_ssd_graph_lib_tf1_test.py | 426 + .../virtuallab/object_detection/exporter.py | 656 + .../object_detection/exporter_lib_tf2_test.py | 297 + .../object_detection/exporter_lib_v2.py | 275 + .../object_detection/exporter_main_v2.py | 159 + .../object_detection/exporter_tf1_test.py | 1206 ++ .../g3doc/challenge_evaluation.md | 215 + .../g3doc/configuring_jobs.md | 147 + .../object_detection/g3doc/context_rcnn.md | 201 + .../g3doc/defining_your_own_model.md | 137 + .../g3doc/evaluation_protocols.md | 163 + .../g3doc/exporting_models.md | 38 + .../virtuallab/object_detection/g3doc/faq.md | 27 + .../g3doc/img/dogs_detections_output.jpg | Bin 0 -> 372894 bytes .../g3doc/img/example_cat.jpg | Bin 0 -> 243955 bytes .../g3doc/img/groupof_case_eval.png | Bin 0 -> 242500 bytes .../g3doc/img/kites_detections_output.jpg | Bin 0 -> 386066 bytes .../g3doc/img/kites_with_segment_overlay.png | Bin 0 -> 7165291 bytes .../g3doc/img/nongroupof_case_eval.png | Bin 0 -> 265455 bytes .../g3doc/img/oid_bus_72e19c28aac34ed8.jpg | Bin 0 -> 249313 bytes .../g3doc/img/oid_monkey_3b4168c89cecbc5b.jpg | Bin 0 -> 281143 bytes .../object_detection/g3doc/img/oxford_pet.png | Bin 0 -> 276715 bytes .../g3doc/img/tensorboard.png | Bin 0 -> 79342 bytes .../g3doc/img/tensorboard2.png | Bin 0 -> 236549 bytes .../g3doc/img/tf-od-api-logo.png | Bin 0 -> 200506 bytes .../g3doc/instance_segmentation.md | 105 + .../g3doc/oid_inference_and_evaluation.md | 257 + .../g3doc/preparing_inputs.md | 59 + .../object_detection/g3doc/release_notes.md | 358 + .../g3doc/running_notebook.md | 18 + .../g3doc/running_on_mobile_tensorflowlite.md | 149 + .../g3doc/running_on_mobile_tf2.md | 144 + .../object_detection/g3doc/running_pets.md | 321 + .../virtuallab/object_detection/g3doc/tf1.md | 94 + .../g3doc/tf1_detection_zoo.md | 189 + .../g3doc/tf1_training_and_evaluation.md | 237 + .../virtuallab/object_detection/g3doc/tf2.md | 87 + .../g3doc/tf2_classification_zoo.md | 25 + .../g3doc/tf2_detection_zoo.md | 67 + .../g3doc/tf2_training_and_evaluation.md | 285 + .../g3doc/tpu_compatibility.md | 196 + .../object_detection/g3doc/tpu_exporters.md | 37 + .../g3doc/using_your_own_dataset.md | 209 + .../object_detection/generate_tfrecord.py | 168 + .../images/test/frame1000.jpg | Bin 0 -> 49896 bytes .../images/test/frame1000.xml | 86 + .../images/test/frame1035.jpg | Bin 0 -> 51248 bytes .../images/test/frame1035.xml | 86 + .../images/test/frame1040.jpg | Bin 0 -> 51704 bytes .../images/test/frame1040.xml | 86 + .../images/test/frame1045.jpg | Bin 0 -> 52084 bytes .../images/test/frame1045.xml | 86 + .../images/test/frame1050.jpg | Bin 0 -> 52169 bytes .../images/test/frame1050.xml | 86 + .../images/test/frame2025.jpg | Bin 0 -> 50001 bytes .../images/test/frame2025.xml | 86 + .../images/test/frame2035.jpg | Bin 0 -> 51081 bytes .../images/test/frame2035.xml | 86 + .../images/test/frame2040.jpg | Bin 0 -> 50660 bytes .../images/test/frame2040.xml | 86 + .../images/test/frame2045.jpg | Bin 0 -> 50071 bytes .../images/test/frame2045.xml | 86 + .../images/test/frame2050.jpg | Bin 0 -> 50303 bytes .../images/test/frame2050.xml | 86 + .../images/test/frame2051.jpg | Bin 0 -> 50395 bytes .../images/test/frame2051.xml | 86 + .../images/test/frame3035.jpg | Bin 0 -> 50644 bytes .../images/test/frame3035.xml | 86 + .../images/test/frame3040.jpg | Bin 0 -> 50133 bytes .../images/test/frame3040.xml | 86 + .../images/test/frame3045.jpg | Bin 0 -> 48920 bytes .../images/test/frame3045.xml | 86 + .../images/test/frame3050.jpg | Bin 0 -> 49075 bytes .../images/test/frame3050.xml | 86 + .../images/test/frame4025.jpg | Bin 0 -> 50446 bytes .../images/test/frame4025.xml | 86 + .../images/test/frame4035.jpg | Bin 0 -> 51825 bytes .../images/test/frame4035.xml | 86 + .../images/test/frame4040.jpg | Bin 0 -> 52033 bytes .../images/test/frame4040.xml | 86 + .../images/test/frame4045.jpg | Bin 0 -> 52639 bytes .../images/test/frame4045.xml | 86 + .../images/test/frame4050.jpg | Bin 0 -> 51336 bytes .../images/test/frame4050.xml | 86 + .../images/train/frame1024.jpg | Bin 0 -> 51338 bytes .../images/train/frame1024.xml | 86 + .../images/train/frame1025.jpg | Bin 0 -> 51448 bytes .../images/train/frame1025.xml | 86 + .../images/train/frame1026.jpg | Bin 0 -> 50568 bytes .../images/train/frame1026.xml | 86 + .../images/train/frame1027.jpg | Bin 0 -> 51626 bytes .../images/train/frame1027.xml | 86 + .../images/train/frame1028.jpg | Bin 0 -> 52370 bytes .../images/train/frame1028.xml | 86 + .../images/train/frame1029.jpg | Bin 0 -> 51388 bytes .../images/train/frame1029.xml | 86 + .../images/train/frame1030.jpg | Bin 0 -> 52500 bytes .../images/train/frame1030.xml | 86 + .../images/train/frame1031.jpg | Bin 0 -> 52391 bytes .../images/train/frame1031.xml | 86 + .../images/train/frame1032.jpg | Bin 0 -> 51659 bytes .../images/train/frame1032.xml | 86 + .../images/train/frame1033.jpg | Bin 0 -> 52505 bytes .../images/train/frame1033.xml | 86 + .../images/train/frame1034.jpg | Bin 0 -> 51944 bytes .../images/train/frame1034.xml | 86 + .../images/train/frame1036.jpg | Bin 0 -> 52269 bytes .../images/train/frame1036.xml | 86 + .../images/train/frame1037.jpg | Bin 0 -> 51430 bytes .../images/train/frame1037.xml | 86 + .../images/train/frame1038.jpg | Bin 0 -> 51534 bytes .../images/train/frame1038.xml | 86 + .../images/train/frame1039.jpg | Bin 0 -> 51964 bytes .../images/train/frame1039.xml | 86 + .../images/train/frame1041.jpg | Bin 0 -> 51695 bytes .../images/train/frame1041.xml | 86 + .../images/train/frame1042.jpg | Bin 0 -> 52178 bytes .../images/train/frame1042.xml | 86 + .../images/train/frame1043.jpg | Bin 0 -> 51395 bytes .../images/train/frame1043.xml | 86 + .../images/train/frame1044.jpg | Bin 0 -> 51781 bytes .../images/train/frame1044.xml | 86 + .../images/train/frame1046.jpg | Bin 0 -> 51668 bytes .../images/train/frame1046.xml | 86 + .../images/train/frame1047.jpg | Bin 0 -> 51938 bytes .../images/train/frame1047.xml | 86 + .../images/train/frame1048.jpg | Bin 0 -> 52183 bytes .../images/train/frame1048.xml | 86 + .../images/train/frame1049.jpg | Bin 0 -> 51323 bytes .../images/train/frame1049.xml | 86 + .../images/train/frame1051.jpg | Bin 0 -> 52079 bytes .../images/train/frame1051.xml | 86 + .../images/train/frame2000.jpg | Bin 0 -> 51228 bytes .../images/train/frame2000.xml | 86 + .../images/train/frame2024.jpg | Bin 0 -> 50592 bytes .../images/train/frame2024.xml | 86 + .../images/train/frame2026.jpg | Bin 0 -> 51209 bytes .../images/train/frame2026.xml | 86 + .../images/train/frame2027.jpg | Bin 0 -> 50709 bytes .../images/train/frame2027.xml | 86 + .../images/train/frame2028.jpg | Bin 0 -> 50783 bytes .../images/train/frame2028.xml | 86 + .../images/train/frame2029.jpg | Bin 0 -> 51197 bytes .../images/train/frame2029.xml | 86 + .../images/train/frame2030.jpg | Bin 0 -> 50406 bytes .../images/train/frame2030.xml | 86 + .../images/train/frame2031.jpg | Bin 0 -> 50595 bytes .../images/train/frame2031.xml | 86 + .../images/train/frame2032.jpg | Bin 0 -> 51199 bytes .../images/train/frame2032.xml | 86 + .../images/train/frame2033.jpg | Bin 0 -> 50254 bytes .../images/train/frame2033.xml | 86 + .../images/train/frame2034.jpg | Bin 0 -> 50760 bytes .../images/train/frame2034.xml | 86 + .../images/train/frame2036.jpg | Bin 0 -> 50208 bytes .../images/train/frame2036.xml | 86 + .../images/train/frame2037.jpg | Bin 0 -> 50568 bytes .../images/train/frame2037.xml | 86 + .../images/train/frame2038.jpg | Bin 0 -> 50760 bytes .../images/train/frame2038.xml | 86 + .../images/train/frame2039.jpg | Bin 0 -> 49953 bytes .../images/train/frame2039.xml | 86 + .../images/train/frame2041.jpg | Bin 0 -> 50672 bytes .../images/train/frame2041.xml | 86 + .../images/train/frame2042.jpg | Bin 0 -> 50041 bytes .../images/train/frame2042.xml | 86 + .../images/train/frame2043.jpg | Bin 0 -> 51294 bytes .../images/train/frame2043.xml | 86 + .../images/train/frame2044.jpg | Bin 0 -> 50547 bytes .../images/train/frame2044.xml | 86 + .../images/train/frame2046.jpg | Bin 0 -> 51093 bytes .../images/train/frame2046.xml | 86 + .../images/train/frame2047.jpg | Bin 0 -> 50534 bytes .../images/train/frame2047.xml | 86 + .../images/train/frame2048.jpg | Bin 0 -> 50116 bytes .../images/train/frame2048.xml | 86 + .../images/train/frame2049.jpg | Bin 0 -> 51241 bytes .../images/train/frame2049.xml | 86 + .../images/train/frame3000.jpg | Bin 0 -> 49857 bytes .../images/train/frame3000.xml | 86 + .../images/train/frame3024.jpg | Bin 0 -> 49738 bytes .../images/train/frame3024.xml | 86 + .../images/train/frame3025.jpg | Bin 0 -> 49271 bytes .../images/train/frame3025.xml | 86 + .../images/train/frame3026.jpg | Bin 0 -> 49826 bytes .../images/train/frame3026.xml | 86 + .../images/train/frame3027.jpg | Bin 0 -> 49616 bytes .../images/train/frame3027.xml | 86 + .../images/train/frame3028.jpg | Bin 0 -> 49319 bytes .../images/train/frame3028.xml | 86 + .../images/train/frame3029.jpg | Bin 0 -> 50201 bytes .../images/train/frame3029.xml | 86 + .../images/train/frame3030.jpg | Bin 0 -> 49192 bytes .../images/train/frame3030.xml | 86 + .../images/train/frame3031.jpg | Bin 0 -> 49224 bytes .../images/train/frame3031.xml | 86 + .../images/train/frame3032.jpg | Bin 0 -> 50301 bytes .../images/train/frame3032.xml | 86 + .../images/train/frame3033.jpg | Bin 0 -> 49071 bytes .../images/train/frame3033.xml | 86 + .../images/train/frame3034.jpg | Bin 0 -> 50216 bytes .../images/train/frame3034.xml | 86 + .../images/train/frame3036.jpg | Bin 0 -> 48618 bytes .../images/train/frame3036.xml | 86 + .../images/train/frame3037.jpg | Bin 0 -> 50439 bytes .../images/train/frame3037.xml | 86 + .../images/train/frame3038.jpg | Bin 0 -> 50486 bytes .../images/train/frame3038.xml | 86 + .../images/train/frame3039.jpg | Bin 0 -> 48677 bytes .../images/train/frame3039.xml | 86 + .../images/train/frame3041.jpg | Bin 0 -> 50308 bytes .../images/train/frame3041.xml | 86 + .../images/train/frame3042.jpg | Bin 0 -> 48740 bytes .../images/train/frame3042.xml | 86 + .../images/train/frame3043.jpg | Bin 0 -> 50239 bytes .../images/train/frame3043.xml | 86 + .../images/train/frame3044.jpg | Bin 0 -> 49873 bytes .../images/train/frame3044.xml | 86 + .../images/train/frame3046.jpg | Bin 0 -> 50608 bytes .../images/train/frame3046.xml | 86 + .../images/train/frame3047.jpg | Bin 0 -> 49964 bytes .../images/train/frame3047.xml | 86 + .../images/train/frame3048.jpg | Bin 0 -> 49475 bytes .../images/train/frame3048.xml | 86 + .../images/train/frame3049.jpg | Bin 0 -> 50462 bytes .../images/train/frame3049.xml | 86 + .../images/train/frame3051.jpg | Bin 0 -> 49255 bytes .../images/train/frame3051.xml | 86 + .../images/train/frame4000.jpg | Bin 0 -> 49236 bytes .../images/train/frame4000.xml | 86 + .../images/train/frame4026.jpg | Bin 0 -> 50873 bytes .../images/train/frame4026.xml | 86 + .../images/train/frame4027.jpg | Bin 0 -> 50565 bytes .../images/train/frame4027.xml | 86 + .../images/train/frame4028.jpg | Bin 0 -> 51278 bytes .../images/train/frame4028.xml | 86 + .../images/train/frame4029.jpg | Bin 0 -> 51818 bytes .../images/train/frame4029.xml | 86 + .../images/train/frame4030.jpg | Bin 0 -> 51141 bytes .../images/train/frame4030.xml | 86 + .../images/train/frame4031.jpg | Bin 0 -> 51624 bytes .../images/train/frame4031.xml | 86 + .../images/train/frame4032.jpg | Bin 0 -> 52235 bytes .../images/train/frame4032.xml | 86 + .../images/train/frame4033.jpg | Bin 0 -> 51646 bytes .../images/train/frame4033.xml | 86 + .../images/train/frame4034.jpg | Bin 0 -> 51899 bytes .../images/train/frame4034.xml | 86 + .../images/train/frame4036.jpg | Bin 0 -> 51279 bytes .../images/train/frame4036.xml | 86 + .../images/train/frame4037.jpg | Bin 0 -> 52031 bytes .../images/train/frame4037.xml | 86 + .../images/train/frame4038.jpg | Bin 0 -> 51703 bytes .../images/train/frame4038.xml | 86 + .../images/train/frame4039.jpg | Bin 0 -> 51909 bytes .../images/train/frame4039.xml | 86 + .../images/train/frame4041.jpg | Bin 0 -> 51868 bytes .../images/train/frame4041.xml | 86 + .../images/train/frame4042.jpg | Bin 0 -> 52370 bytes .../images/train/frame4042.xml | 86 + .../images/train/frame4043.jpg | Bin 0 -> 52060 bytes .../images/train/frame4043.xml | 86 + .../images/train/frame4044.jpg | Bin 0 -> 51574 bytes .../images/train/frame4044.xml | 86 + .../images/train/frame4046.jpg | Bin 0 -> 52177 bytes .../images/train/frame4046.xml | 86 + .../images/train/frame4047.jpg | Bin 0 -> 52303 bytes .../images/train/frame4047.xml | 86 + .../images/train/frame4048.jpg | Bin 0 -> 52802 bytes .../images/train/frame4048.xml | 86 + .../images/train/frame4049.jpg | Bin 0 -> 52024 bytes .../images/train/frame4049.xml | 86 + .../images/train/frame4051.jpg | Bin 0 -> 51816 bytes .../images/train/frame4051.xml | 86 + .../images/train/frame4052.jpg | Bin 0 -> 51795 bytes .../images/train/frame4052.xml | 86 + .../object_detection/inference/__init__.py | 0 .../inference/detection_inference.py | 141 + .../inference/detection_inference_tf1_test.py | 177 + .../inference/infer_detections.py | 96 + .../virtuallab/object_detection/inputs.py | 1143 ++ .../object_detection/inputs_test.py | 1665 ++ .../object_detection/legacy/__init__.py | 0 .../object_detection/legacy/__init__.pyc | Bin 0 -> 139 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 135 bytes .../legacy/__pycache__/trainer.cpython-36.pyc | Bin 0 -> 11419 bytes .../object_detection/legacy/eval.py | 142 + .../object_detection/legacy/evaluator.py | 298 + .../object_detection/legacy/train.py | 186 + .../object_detection/legacy/trainer.py | 415 + .../object_detection/legacy/trainer.pyc | Bin 0 -> 13257 bytes .../legacy/trainer_tf1_test.py | 295 + .../object_detection/matchers/__init__.py | 0 .../object_detection/matchers/__init__.pyc | Bin 0 -> 141 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 146 bytes .../__pycache__/argmax_matcher.cpython-36.pyc | Bin 0 -> 7084 bytes .../bipartite_matcher.cpython-36.pyc | Bin 0 -> 2253 bytes .../hungarian_matcher.cpython-36.pyc | Bin 0 -> 2072 bytes .../matchers/argmax_matcher.py | 208 + .../matchers/argmax_matcher.pyc | Bin 0 -> 7758 bytes .../matchers/argmax_matcher_test.py | 235 + .../matchers/bipartite_matcher.py | 70 + .../matchers/bipartite_matcher.pyc | Bin 0 -> 2553 bytes .../matchers/bipartite_matcher_tf1_test.py | 92 + .../matchers/hungarian_matcher.py | 58 + .../matchers/hungarian_matcher.pyc | Bin 0 -> 2355 bytes .../matchers/hungarian_matcher_tf2_test.py | 105 + .../meta_architectures/__init__.py | 0 .../meta_architectures/__init__.pyc | Bin 0 -> 151 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 156 bytes .../center_net_meta_arch.cpython-36.pyc | Bin 0 -> 115546 bytes .../context_rcnn_lib.cpython-36.pyc | Bin 0 -> 6424 bytes .../context_rcnn_lib_tf2.cpython-36.pyc | Bin 0 -> 7436 bytes .../context_rcnn_meta_arch.cpython-36.pyc | Bin 0 -> 14559 bytes .../faster_rcnn_meta_arch.cpython-36.pyc | Bin 0 -> 104468 bytes .../__pycache__/rfcn_meta_arch.cpython-36.pyc | Bin 0 -> 15914 bytes .../__pycache__/ssd_meta_arch.cpython-36.pyc | Bin 0 -> 47494 bytes .../center_net_meta_arch.py | 3505 ++++ .../center_net_meta_arch.pyc | Bin 0 -> 125490 bytes .../center_net_meta_arch_tf2_test.py | 2274 +++ .../meta_architectures/context_rcnn_lib.py | 224 + .../meta_architectures/context_rcnn_lib.pyc | Bin 0 -> 7387 bytes .../context_rcnn_lib_tf1_test.py | 126 + .../context_rcnn_lib_tf2.py | 239 + .../context_rcnn_lib_tf2.pyc | Bin 0 -> 8843 bytes .../context_rcnn_lib_tf2_test.py | 120 + .../context_rcnn_meta_arch.py | 351 + .../context_rcnn_meta_arch.pyc | Bin 0 -> 15258 bytes .../context_rcnn_meta_arch_test.py | 541 + .../faster_rcnn_meta_arch.py | 2906 ++++ .../faster_rcnn_meta_arch.pyc | Bin 0 -> 112075 bytes .../faster_rcnn_meta_arch_test.py | 513 + .../faster_rcnn_meta_arch_test_lib.py | 2182 +++ .../meta_architectures/rfcn_meta_arch.py | 389 + .../meta_architectures/rfcn_meta_arch.pyc | Bin 0 -> 16711 bytes .../meta_architectures/rfcn_meta_arch_test.py | 67 + .../meta_architectures/ssd_meta_arch.py | 1355 ++ .../meta_architectures/ssd_meta_arch.pyc | Bin 0 -> 51098 bytes .../meta_architectures/ssd_meta_arch_test.py | 680 + .../ssd_meta_arch_test_lib.py | 259 + .../object_detection/metrics/__init__.py | 0 .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 136 bytes .../coco_evaluation.cpython-36.pyc | Bin 0 -> 61375 bytes .../__pycache__/coco_tools.cpython-36.pyc | Bin 0 -> 35487 bytes .../lvis_evaluation.cpython-36.pyc | Bin 0 -> 14284 bytes .../metrics/calibration_evaluation.py | 228 + .../calibration_evaluation_tf1_test.py | 203 + .../metrics/calibration_metrics.py | 118 + .../metrics/calibration_metrics_tf1_test.py | 112 + .../metrics/coco_evaluation.py | 1875 ++ .../metrics/coco_evaluation_test.py | 2106 +++ .../object_detection/metrics/coco_tools.py | 973 ++ .../metrics/coco_tools_test.py | 405 + .../object_detection/metrics/io_utils.py | 34 + .../metrics/lvis_evaluation.py | 443 + .../metrics/lvis_evaluation_test.py | 182 + .../object_detection/metrics/lvis_tools.py | 259 + .../metrics/lvis_tools_test.py | 158 + .../metrics/offline_eval_map_corloc.py | 171 + .../metrics/offline_eval_map_corloc_test.py | 58 + .../metrics/oid_challenge_evaluation.py | 149 + .../metrics/oid_challenge_evaluation_utils.py | 197 + .../oid_challenge_evaluation_utils_test.py | 308 + .../metrics/oid_vrd_challenge_evaluation.py | 154 + .../oid_vrd_challenge_evaluation_utils.py | 125 + ...oid_vrd_challenge_evaluation_utils_test.py | 149 + .../metrics/tf_example_parser.py | 159 + .../metrics/tf_example_parser_test.py | 197 + .../object_detection/model_hparams.py | 50 + .../virtuallab/object_detection/model_lib.py | 1097 ++ .../object_detection/model_lib_tf1_test.py | 505 + .../object_detection/model_lib_tf2_test.py | 230 + .../object_detection/model_lib_v2.py | 986 ++ .../virtuallab/object_detection/model_main.py | 108 + .../object_detection/model_main_tf2.py | 113 + .../object_detection/model_tpu_main.py | 139 + .../object_detection/models/__init__.py | 0 .../object_detection/models/__init__.pyc | Bin 0 -> 139 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 144 bytes ...ilenet_v1_feature_extractor.cpython-36.pyc | Bin 0 -> 5543 bytes ...resnet_v2_feature_extractor.cpython-36.pyc | Bin 0 -> 7428 bytes ...eption_v2_feature_extractor.cpython-36.pyc | Bin 0 -> 7187 bytes ..._rcnn_nas_feature_extractor.cpython-36.pyc | Bin 0 -> 8394 bytes ...rcnn_pnas_feature_extractor.cpython-36.pyc | Bin 0 -> 8778 bytes ...resnet_v1_feature_extractor.cpython-36.pyc | Bin 0 -> 8728 bytes .../feature_map_generators.cpython-36.pyc | Bin 0 -> 25335 bytes ...eption_v2_feature_extractor.cpython-36.pyc | Bin 0 -> 4510 bytes ...eption_v3_feature_extractor.cpython-36.pyc | Bin 0 -> 4523 bytes ...mobiledet_feature_extractor.cpython-36.pyc | Bin 0 -> 15577 bytes ...t_edgetpu_feature_extractor.cpython-36.pyc | Bin 0 -> 1250 bytes ...ilenet_v1_feature_extractor.cpython-36.pyc | Bin 0 -> 4465 bytes ...et_v1_fpn_feature_extractor.cpython-36.pyc | Bin 0 -> 7018 bytes ...et_v1_ppn_feature_extractor.cpython-36.pyc | Bin 0 -> 2785 bytes ...ilenet_v2_feature_extractor.cpython-36.pyc | Bin 0 -> 4626 bytes ...et_v2_fpn_feature_extractor.cpython-36.pyc | Bin 0 -> 6944 bytes ...2_mnasfpn_feature_extractor.cpython-36.pyc | Bin 0 -> 11441 bytes ...ilenet_v3_feature_extractor.cpython-36.pyc | Bin 0 -> 6349 bytes ...d_pnasnet_feature_extractor.cpython-36.pyc | Bin 0 -> 5983 bytes ...et_v1_fpn_feature_extractor.cpython-36.pyc | Bin 0 -> 13427 bytes ...et_v1_ppn_feature_extractor.cpython-36.pyc | Bin 0 -> 10140 bytes ...idirectional_feature_pyramid_generators.py | 486 + ...nal_feature_pyramid_generators_tf2_test.py | 167 + .../center_net_hourglass_feature_extractor.py | 82 + ...et_hourglass_feature_extractor_tf2_test.py | 45 + ...nter_net_mobilenet_v2_feature_extractor.py | 122 + ...mobilenet_v2_feature_extractor_tf2_test.py | 46 + ..._net_mobilenet_v2_fpn_feature_extractor.py | 142 + ...lenet_v2_fpn_feature_extractor_tf2_test.py | 46 + .../center_net_resnet_feature_extractor.py | 157 + ...r_net_resnet_feature_extractor_tf2_test.py | 54 + ...ter_net_resnet_v1_fpn_feature_extractor.py | 214 + ...esnet_v1_fpn_feature_extractor_tf2_test.py | 51 + ...dded_ssd_mobilenet_v1_feature_extractor.py | 165 + ...ded_ssd_mobilenet_v1_feature_extractor.pyc | Bin 0 -> 6221 bytes ...mobilenet_v1_feature_extractor_tf1_test.py | 132 + ...n_inception_resnet_v2_feature_extractor.py | 212 + ..._inception_resnet_v2_feature_extractor.pyc | Bin 0 -> 8232 bytes ...on_resnet_v2_feature_extractor_tf1_test.py | 111 + ...ption_resnet_v2_keras_feature_extractor.py | 159 + ...net_v2_keras_feature_extractor_tf2_test.py | 80 + ...ter_rcnn_inception_v2_feature_extractor.py | 253 + ...er_rcnn_inception_v2_feature_extractor.pyc | Bin 0 -> 8463 bytes ...inception_v2_feature_extractor_tf1_test.py | 128 + ...ter_rcnn_mobilenet_v1_feature_extractor.py | 193 + ...mobilenet_v1_feature_extractor_tf1_test.py | 128 + .../faster_rcnn_nas_feature_extractor.py | 336 + .../faster_rcnn_nas_feature_extractor.pyc | Bin 0 -> 9594 bytes ...ter_rcnn_nas_feature_extractor_tf1_test.py | 111 + .../faster_rcnn_pnas_feature_extractor.py | 329 + .../faster_rcnn_pnas_feature_extractor.pyc | Bin 0 -> 10025 bytes ...er_rcnn_pnas_feature_extractor_tf1_test.py | 124 + ...ter_rcnn_resnet_keras_feature_extractor.py | 254 + ...resnet_keras_feature_extractor_tf2_test.py | 80 + ...faster_rcnn_resnet_v1_feature_extractor.py | 268 + ...aster_rcnn_resnet_v1_feature_extractor.pyc | Bin 0 -> 9847 bytes ...nn_resnet_v1_feature_extractor_tf1_test.py | 167 + ...n_resnet_v1_fpn_keras_feature_extractor.py | 434 + ...v1_fpn_keras_feature_extractor_tf2_test.py | 94 + .../models/feature_map_generators.py | 825 + .../models/feature_map_generators.pyc | Bin 0 -> 27353 bytes .../models/feature_map_generators_test.py | 842 + .../models/keras_models/__init__.py | 0 .../base_models/original_mobilenet_v2.py | 478 + .../keras_models/convert_keras_models.py | 85 + .../models/keras_models/hourglass_network.py | 624 + .../hourglass_network_tf2_test.py | 158 + .../keras_models/inception_resnet_v2.py | 244 + .../inception_resnet_v2_tf2_test.py | 228 + .../models/keras_models/mobilenet_v1.py | 358 + .../keras_models/mobilenet_v1_tf2_test.py | 256 + .../models/keras_models/mobilenet_v2.py | 334 + .../keras_models/mobilenet_v2_tf2_test.py | 250 + .../models/keras_models/model_utils.py | 53 + .../models/keras_models/resnet_v1.py | 541 + .../models/keras_models/resnet_v1_tf2_test.py | 226 + .../models/keras_models/test_utils.py | 214 + ...sd_efficientnet_bifpn_feature_extractor.py | 925 + ...entnet_bifpn_feature_extractor_tf2_test.py | 179 + .../models/ssd_feature_extractor_test.py | 263 + .../ssd_inception_v2_feature_extractor.py | 137 + .../ssd_inception_v2_feature_extractor.pyc | Bin 0 -> 5071 bytes ...inception_v2_feature_extractor_tf1_test.py | 160 + .../ssd_inception_v3_feature_extractor.py | 137 + .../ssd_inception_v3_feature_extractor.pyc | Bin 0 -> 5082 bytes ...inception_v3_feature_extractor_tf1_test.py | 160 + .../models/ssd_mobiledet_feature_extractor.py | 586 + .../ssd_mobiledet_feature_extractor.pyc | Bin 0 -> 19690 bytes ...sd_mobiledet_feature_extractor_tf1_test.py | 172 + ...ssd_mobilenet_edgetpu_feature_extractor.py | 49 + ...sd_mobilenet_edgetpu_feature_extractor.pyc | Bin 0 -> 1516 bytes ...enet_edgetpu_feature_extractor_testbase.py | 112 + ...enet_edgetpu_feature_extractor_tf1_test.py | 65 + .../ssd_mobilenet_v1_feature_extractor.py | 138 + .../ssd_mobilenet_v1_feature_extractor.pyc | Bin 0 -> 5049 bytes ...mobilenet_v1_feature_extractor_tf1_test.py | 272 + ...mobilenet_v1_feature_extractor_tf2_test.py | 248 + .../ssd_mobilenet_v1_fpn_feature_extractor.py | 202 + ...ssd_mobilenet_v1_fpn_feature_extractor.pyc | Bin 0 -> 7891 bytes ...lenet_v1_fpn_feature_extractor_tf1_test.py | 206 + ...lenet_v1_fpn_feature_extractor_tf2_test.py | 179 + ...obilenet_v1_fpn_keras_feature_extractor.py | 256 + ...sd_mobilenet_v1_keras_feature_extractor.py | 165 + .../ssd_mobilenet_v1_ppn_feature_extractor.py | 84 + ...ssd_mobilenet_v1_ppn_feature_extractor.pyc | Bin 0 -> 3258 bytes ...lenet_v1_ppn_feature_extractor_tf1_test.py | 186 + .../ssd_mobilenet_v2_feature_extractor.py | 140 + .../ssd_mobilenet_v2_feature_extractor.pyc | Bin 0 -> 5218 bytes ...mobilenet_v2_feature_extractor_tf1_test.py | 196 + ...mobilenet_v2_feature_extractor_tf2_test.py | 192 + .../ssd_mobilenet_v2_fpn_feature_extractor.py | 199 + ...ssd_mobilenet_v2_fpn_feature_extractor.pyc | Bin 0 -> 7801 bytes ...lenet_v2_fpn_feature_extractor_tf1_test.py | 372 + ...lenet_v2_fpn_feature_extractor_tf2_test.py | 269 + ...obilenet_v2_fpn_keras_feature_extractor.py | 243 + ...sd_mobilenet_v2_keras_feature_extractor.py | 168 + ..._mobilenet_v2_mnasfpn_feature_extractor.py | 412 + ...mobilenet_v2_mnasfpn_feature_extractor.pyc | Bin 0 -> 13680 bytes ...t_v2_mnasfpn_feature_extractor_tf1_test.py | 87 + .../ssd_mobilenet_v3_feature_extractor.py | 218 + .../ssd_mobilenet_v3_feature_extractor.pyc | Bin 0 -> 7337 bytes ...mobilenet_v3_feature_extractor_testbase.py | 112 + ...mobilenet_v3_feature_extractor_tf1_test.py | 105 + .../models/ssd_pnasnet_feature_extractor.py | 182 + .../models/ssd_pnasnet_feature_extractor.pyc | Bin 0 -> 6737 bytes .../ssd_pnasnet_feature_extractor_tf1_test.py | 108 + .../ssd_resnet_v1_fpn_feature_extractor.py | 391 + .../ssd_resnet_v1_fpn_feature_extractor.pyc | Bin 0 -> 14711 bytes ...esnet_v1_fpn_feature_extractor_testbase.py | 193 + ...esnet_v1_fpn_feature_extractor_tf1_test.py | 85 + ...esnet_v1_fpn_feature_extractor_tf2_test.py | 103 + ...d_resnet_v1_fpn_keras_feature_extractor.py | 457 + .../ssd_resnet_v1_ppn_feature_extractor.py | 284 + .../ssd_resnet_v1_ppn_feature_extractor.pyc | Bin 0 -> 11277 bytes ...esnet_v1_ppn_feature_extractor_testbase.py | 82 + ...esnet_v1_ppn_feature_extractor_tf1_test.py | 93 + .../object_detection/packages/tf1/setup.py | 27 + .../object_detection/packages/tf2/setup.py | 44 + .../object_detection/predictors/__init__.py | 0 .../object_detection/predictors/__init__.pyc | Bin 0 -> 143 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 148 bytes ...convolutional_box_predictor.cpython-36.pyc | Bin 0 -> 13875 bytes ...utional_keras_box_predictor.cpython-36.pyc | Bin 0 -> 15208 bytes .../mask_rcnn_box_predictor.cpython-36.pyc | Bin 0 -> 5130 bytes ...sk_rcnn_keras_box_predictor.cpython-36.pyc | Bin 0 -> 5195 bytes .../rfcn_box_predictor.cpython-36.pyc | Bin 0 -> 5014 bytes .../rfcn_keras_box_predictor.cpython-36.pyc | Bin 0 -> 5797 bytes .../predictors/convolutional_box_predictor.py | 421 + .../convolutional_box_predictor.pyc | Bin 0 -> 15296 bytes .../convolutional_box_predictor_tf1_test.py | 932 + .../convolutional_keras_box_predictor.py | 486 + .../convolutional_keras_box_predictor.pyc | Bin 0 -> 17055 bytes ...volutional_keras_box_predictor_tf2_test.py | 952 ++ .../predictors/heads/__init__.py | 0 .../predictors/heads/__init__.pyc | Bin 0 -> 149 bytes .../heads/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 154 bytes .../heads/__pycache__/box_head.cpython-36.pyc | Bin 0 -> 8755 bytes .../__pycache__/class_head.cpython-36.pyc | Bin 0 -> 9968 bytes .../heads/__pycache__/head.cpython-36.pyc | Bin 0 -> 2643 bytes .../__pycache__/keras_box_head.cpython-36.pyc | Bin 0 -> 10292 bytes .../keras_class_head.cpython-36.pyc | Bin 0 -> 10981 bytes .../keras_mask_head.cpython-36.pyc | Bin 0 -> 14026 bytes .../__pycache__/mask_head.cpython-36.pyc | Bin 0 -> 11339 bytes .../predictors/heads/box_head.py | 281 + .../predictors/heads/box_head.pyc | Bin 0 -> 9813 bytes .../predictors/heads/box_head_tf1_test.py | 132 + .../predictors/heads/class_head.py | 315 + .../predictors/heads/class_head.pyc | Bin 0 -> 11031 bytes .../predictors/heads/class_head_tf1_test.py | 199 + .../object_detection/predictors/heads/head.py | 81 + .../predictors/heads/head.pyc | Bin 0 -> 3029 bytes .../predictors/heads/keras_box_head.py | 333 + .../predictors/heads/keras_box_head.pyc | Bin 0 -> 11430 bytes .../heads/keras_box_head_tf2_test.py | 199 + .../predictors/heads/keras_class_head.py | 351 + .../predictors/heads/keras_class_head.pyc | Bin 0 -> 12096 bytes .../heads/keras_class_head_tf2_test.py | 203 + .../predictors/heads/keras_mask_head.py | 447 + .../predictors/heads/keras_mask_head.pyc | Bin 0 -> 15550 bytes .../heads/keras_mask_head_tf2_test.py | 252 + .../predictors/heads/keypoint_head.py | 115 + .../heads/keypoint_head_tf1_test.py | 60 + .../predictors/heads/mask_head.py | 360 + .../predictors/heads/mask_head.pyc | Bin 0 -> 12600 bytes .../predictors/heads/mask_head_tf1_test.py | 190 + .../predictors/mask_rcnn_box_predictor.py | 141 + .../predictors/mask_rcnn_box_predictor.pyc | Bin 0 -> 5593 bytes .../mask_rcnn_box_predictor_tf1_test.py | 154 + .../mask_rcnn_keras_box_predictor.py | 139 + .../mask_rcnn_keras_box_predictor.pyc | Bin 0 -> 5660 bytes .../mask_rcnn_keras_box_predictor_tf2_test.py | 144 + .../predictors/rfcn_box_predictor.py | 159 + .../predictors/rfcn_box_predictor.pyc | Bin 0 -> 5632 bytes .../predictors/rfcn_box_predictor_tf1_test.py | 80 + .../predictors/rfcn_keras_box_predictor.py | 204 + .../predictors/rfcn_keras_box_predictor.pyc | Bin 0 -> 6555 bytes .../rfcn_keras_box_predictor_tf2_test.py | 79 + .../object_detection/protos/__init__.py | 0 .../object_detection/protos/__init__.pyc | Bin 0 -> 139 bytes .../protos/__init__/py_pb2.py | 30 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 144 bytes .../anchor_generator_pb2.cpython-36.pyc | Bin 0 -> 4026 bytes .../argmax_matcher_pb2.cpython-36.pyc | Bin 0 -> 2792 bytes .../bipartite_matcher_pb2.cpython-36.pyc | Bin 0 -> 1904 bytes .../__pycache__/box_coder_pb2.cpython-36.pyc | Bin 0 -> 3737 bytes .../box_predictor_pb2.cpython-36.pyc | Bin 0 -> 14329 bytes .../calibration_pb2.cpython-36.pyc | Bin 0 -> 9827 bytes .../__pycache__/center_net_pb2.cpython-36.pyc | Bin 0 -> 17713 bytes .../__pycache__/eval_pb2.cpython-36.pyc | Bin 0 -> 10623 bytes .../faster_rcnn_box_coder_pb2.cpython-36.pyc | Bin 0 -> 2357 bytes .../faster_rcnn_pb2.cpython-36.pyc | Bin 0 -> 13790 bytes ...e_grid_anchor_generator_pb2.cpython-36.pyc | Bin 0 -> 3331 bytes .../protos/__pycache__/fpn_pb2.cpython-36.pyc | Bin 0 -> 3359 bytes .../graph_rewriter_pb2.cpython-36.pyc | Bin 0 -> 2778 bytes .../grid_anchor_generator_pb2.cpython-36.pyc | Bin 0 -> 2941 bytes .../hyperparams_pb2.cpython-36.pyc | Bin 0 -> 10281 bytes .../image_resizer_pb2.cpython-36.pyc | Bin 0 -> 8425 bytes .../input_reader_pb2.cpython-36.pyc | Bin 0 -> 9847 bytes .../keypoint_box_coder_pb2.cpython-36.pyc | Bin 0 -> 2480 bytes .../__pycache__/losses_pb2.cpython-36.pyc | Bin 0 -> 16014 bytes .../__pycache__/matcher_pb2.cpython-36.pyc | Bin 0 -> 2763 bytes .../mean_stddev_box_coder_pb2.cpython-36.pyc | Bin 0 -> 1911 bytes .../__pycache__/model_pb2.cpython-36.pyc | Bin 0 -> 3699 bytes ...iscale_anchor_generator_pb2.cpython-36.pyc | Bin 0 -> 2803 bytes .../__pycache__/optimizer_pb2.cpython-36.pyc | Bin 0 -> 11076 bytes .../__pycache__/pipeline_pb2.cpython-36.pyc | Bin 0 -> 3809 bytes .../post_processing_pb2.cpython-36.pyc | Bin 0 -> 5855 bytes .../preprocessor_pb2.cpython-36.pyc | Bin 0 -> 45634 bytes ...n_similarity_calculator_pb2.cpython-36.pyc | Bin 0 -> 4392 bytes .../square_box_coder_pb2.cpython-36.pyc | Bin 0 -> 2155 bytes .../ssd_anchor_generator_pb2.cpython-36.pyc | Bin 0 -> 3868 bytes .../protos/__pycache__/ssd_pb2.cpython-36.pyc | Bin 0 -> 12068 bytes .../string_int_label_map_pb2.cpython-36.pyc | Bin 0 -> 4773 bytes .../__pycache__/train_pb2.cpython-36.pyc | Bin 0 -> 8116 bytes .../protos/anchor_generator.proto | 19 + .../protos/anchor_generator_pb2.py | 114 + .../protos/anchor_generator_pb2.pyc | Bin 0 -> 4938 bytes .../protos/argmax_matcher.proto | 29 + .../protos/argmax_matcher_pb2.py | 104 + .../protos/argmax_matcher_pb2.pyc | Bin 0 -> 3688 bytes .../protos/bipartite_matcher.proto | 11 + .../protos/bipartite_matcher_pb2.py | 69 + .../protos/bipartite_matcher_pb2.pyc | Bin 0 -> 2468 bytes .../object_detection/protos/box_coder.proto | 19 + .../object_detection/protos/box_coder_pb2.py | 114 + .../object_detection/protos/box_coder_pb2.pyc | Bin 0 -> 4635 bytes .../protos/box_predictor.proto | 200 + .../protos/box_predictor_pb2.py | 678 + .../protos/box_predictor_pb2.pyc | Bin 0 -> 19083 bytes .../object_detection/protos/calibration.proto | 90 + .../protos/calibration_pb2.py | 589 + .../protos/calibration_pb2.pyc | Bin 0 -> 12854 bytes .../object_detection/protos/center_net.proto | 296 + .../object_detection/protos/center_net_pb2.py | 855 + .../protos/center_net_pb2.pyc | Bin 0 -> 23830 bytes .../object_detection/protos/eval.proto | 164 + .../object_detection/protos/eval_pb2.py | 521 + .../object_detection/protos/eval_pb2.pyc | Bin 0 -> 14441 bytes .../object_detection/protos/faster_rcnn.proto | 233 + .../protos/faster_rcnn_box_coder.proto | 17 + .../protos/faster_rcnn_box_coder_pb2.py | 90 + .../protos/faster_rcnn_box_coder_pb2.pyc | Bin 0 -> 3141 bytes .../protos/faster_rcnn_pb2.py | 509 + .../protos/faster_rcnn_pb2.pyc | Bin 0 -> 18171 bytes .../flexible_grid_anchor_generator.proto | 30 + .../flexible_grid_anchor_generator_pb2.py | 151 + .../flexible_grid_anchor_generator_pb2.pyc | Bin 0 -> 4490 bytes .../object_detection/protos/fpn.proto | 50 + .../object_detection/protos/fpn_pb2.py | 150 + .../object_detection/protos/fpn_pb2.pyc | Bin 0 -> 4465 bytes .../protos/graph_rewriter.proto | 27 + .../protos/graph_rewriter_pb2.py | 130 + .../protos/graph_rewriter_pb2.pyc | Bin 0 -> 3704 bytes .../protos/grid_anchor_generator.proto | 34 + .../protos/grid_anchor_generator_pb2.py | 118 + .../protos/grid_anchor_generator_pb2.pyc | Bin 0 -> 3978 bytes .../object_detection/protos/hyperparams.proto | 136 + .../protos/hyperparams_pb2.py | 665 + .../protos/hyperparams_pb2.pyc | Bin 0 -> 13968 bytes .../protos/image_resizer.proto | 108 + .../protos/image_resizer_pb2.py | 461 + .../protos/image_resizer_pb2.pyc | Bin 0 -> 11140 bytes .../protos/input_reader.proto | 182 + .../protos/input_reader_pb2.py | 455 + .../protos/input_reader_pb2.pyc | Bin 0 -> 13271 bytes .../protos/keypoint_box_coder.proto | 19 + .../protos/keypoint_box_coder_pb2.py | 97 + .../protos/keypoint_box_coder_pb2.pyc | Bin 0 -> 3321 bytes .../object_detection/protos/losses.proto | 219 + .../object_detection/protos/losses_pb2.py | 959 ++ .../object_detection/protos/losses_pb2.pyc | Bin 0 -> 21287 bytes .../object_detection/protos/matcher.proto | 15 + .../object_detection/protos/matcher_pb2.py | 90 + .../object_detection/protos/matcher_pb2.pyc | Bin 0 -> 3455 bytes .../protos/mean_stddev_box_coder.proto | 10 + .../protos/mean_stddev_box_coder_pb2.py | 69 + .../protos/mean_stddev_box_coder_pb2.pyc | Bin 0 -> 2488 bytes .../object_detection/protos/model.proto | 27 + .../object_detection/protos/model_pb2.py | 152 + .../object_detection/protos/model_pb2.pyc | Bin 0 -> 4740 bytes .../protos/multiscale_anchor_generator.proto | 26 + .../protos/multiscale_anchor_generator_pb2.py | 104 + .../multiscale_anchor_generator_pb2.pyc | Bin 0 -> 3723 bytes .../object_detection/protos/optimizer.proto | 96 + .../object_detection/protos/optimizer_pb2.py | 633 + .../object_detection/protos/optimizer_pb2.pyc | Bin 0 -> 14925 bytes .../object_detection/protos/pipeline.proto | 22 + .../object_detection/protos/pipeline_pb2.py | 116 + .../object_detection/protos/pipeline_pb2.pyc | Bin 0 -> 4818 bytes .../protos/post_processing.proto | 95 + .../protos/post_processing_pb2.py | 246 + .../protos/post_processing_pb2.pyc | Bin 0 -> 7703 bytes .../protos/preprocessor.proto | 592 + .../protos/preprocessor_pb2.py | 2781 +++ .../protos/preprocessor_pb2.pyc | Bin 0 -> 61220 bytes .../protos/region_similarity_calculator.proto | 33 + .../region_similarity_calculator_pb2.py | 244 + .../region_similarity_calculator_pb2.pyc | Bin 0 -> 5710 bytes .../protos/square_box_coder.proto | 14 + .../protos/square_box_coder_pb2.py | 83 + .../protos/square_box_coder_pb2.pyc | Bin 0 -> 2860 bytes .../object_detection/protos/ssd.proto | 206 + .../protos/ssd_anchor_generator.proto | 55 + .../protos/ssd_anchor_generator_pb2.py | 153 + .../protos/ssd_anchor_generator_pb2.pyc | Bin 0 -> 5251 bytes .../object_detection/protos/ssd_pb2.py | 468 + .../object_detection/protos/ssd_pb2.pyc | Bin 0 -> 15972 bytes .../protos/string_int_label_map.proto | 57 + .../protos/string_int_label_map_pb2.py | 243 + .../protos/string_int_label_map_pb2.pyc | Bin 0 -> 6326 bytes .../protos/target_assigner.proto | 14 + .../protos/target_assigner_pb2.py | 90 + .../object_detection/protos/train.proto | 138 + .../object_detection/protos/train_pb2.py | 306 + .../object_detection/protos/train_pb2.pyc | Bin 0 -> 10787 bytes .../object_detection/samples/cloud/cloud.yml | 11 + ...t_rcnn_resnet101_snapshot_serengeti.config | 164 + ...n_resnet101_snapshot_serengeti_sync.config | 166 + .../embedded_ssd_mobilenet_v1_coco.config | 186 + ..._v2_quantized_320x320_open_image_v4.config | 211 + ...cnn_inception_resnet_v2_atrous_coco.config | 142 + ...ion_resnet_v2_atrous_cosine_lr_coco.config | 132 + ...rcnn_inception_resnet_v2_atrous_oid.config | 142 + ...n_inception_resnet_v2_atrous_oid_v4.config | 140 + ...cnn_inception_resnet_v2_atrous_pets.config | 141 + .../faster_rcnn_inception_v2_coco.config | 141 + .../faster_rcnn_inception_v2_pets.config | 140 + .../configs/faster_rcnn_nas_coco.config | 142 + .../faster_rcnn_resnet101_atrous_coco.config | 136 + .../faster_rcnn_resnet101_ava_v2.1.config | 138 + .../configs/faster_rcnn_resnet101_coco.config | 135 + .../configs/faster_rcnn_resnet101_fgvc.config | 135 + .../faster_rcnn_resnet101_kitti.config | 138 + .../configs/faster_rcnn_resnet101_pets.config | 139 + .../faster_rcnn_resnet101_voc07.config | 133 + .../configs/faster_rcnn_resnet152_coco.config | 140 + .../configs/faster_rcnn_resnet152_pets.config | 139 + .../configs/faster_rcnn_resnet50_coco.config | 140 + .../configs/faster_rcnn_resnet50_fgvc.config | 135 + .../configs/faster_rcnn_resnet50_pets.config | 140 + ...cnn_inception_resnet_v2_atrous_coco.config | 166 + .../mask_rcnn_inception_v2_coco.config | 165 + .../mask_rcnn_resnet101_atrous_coco.config | 166 + .../configs/mask_rcnn_resnet101_pets.config | 156 + .../mask_rcnn_resnet50_atrous_coco.config | 166 + .../configs/rfcn_resnet101_coco.config | 137 + .../configs/rfcn_resnet101_pets.config | 136 + .../configs/ssd_inception_v2_coco.config | 189 + .../configs/ssd_inception_v2_pets.config | 189 + .../configs/ssd_inception_v3_pets.config | 188 + ...t_v1_0.75_depth_300x300_coco14_sync.config | 198 + ...depth_quantized_300x300_coco14_sync.config | 201 + ...5_depth_quantized_300x300_pets_sync.config | 204 + ...sd_mobilenet_v1_300x300_coco14_sync.config | 197 + .../configs/ssd_mobilenet_v1_coco.config | 194 + .../ssd_mobilenet_v1_focal_loss_pets.config | 191 + ...ilenet_v1_focal_loss_pets_inference.config | 192 + ...d_box_predictor_640x640_coco14_sync.config | 196 + .../configs/ssd_mobilenet_v1_pets.config | 193 + ...d_box_predictor_300x300_coco14_sync.config | 191 + ...et_v1_quantized_300x300_coco14_sync.config | 201 + .../configs/ssd_mobilenet_v2_coco.config | 194 + ...x256_depthmultiplier_75_coco14_sync.config | 208 + .../ssd_mobilenet_v2_fullyconv_coco.config | 207 + ...red_box_predictor_320x320_coco_sync.config | 198 + .../configs/ssd_mobilenet_v2_oid_v4.config | 190 + .../ssd_mobilenet_v2_pets_keras.config | 190 + ...mobilenet_v2_quantized_300x300_coco.config | 202 + ...ared_box_predictor_oid_512x512_sync.config | 194 + ...d_box_predictor_640x640_coco14_sync.config | 197 + ...mobiledet_cpu_320x320_coco_sync_4x4.config | 202 + ...mobiledet_dsp_320x320_coco_sync_4x4.config | 202 + ...ledet_edgetpu_320x320_coco_sync_4x4.config | 202 + ...mobiledet_gpu_320x320_coco_sync_4x4.config | 204 + ...lite_mobilenet_edgetpu_320x320_coco.config | 198 + ...obilenet_edgetpu_320x320_coco_quant.config | 206 + .../configs/ssdlite_mobilenet_v1_coco.config | 196 + .../configs/ssdlite_mobilenet_v2_coco.config | 196 + ...ite_mobilenet_v3_large_320x320_coco.config | 200 + ...ite_mobilenet_v3_small_320x320_coco.config | 199 + .../test_data/context_rcnn_camera_trap.config | 161 + .../test_data/pets_examples.record | Bin 0 -> 873919 bytes ...napshot_serengeti_sequence_examples.record | Bin 0 -> 820685 bytes .../test_data/ssd_mobilenet_v1_fpp.config | 251 + .../test_images/ducky/test/out1.jpg | Bin 0 -> 42242 bytes .../test_images/ducky/test/out10.jpg | Bin 0 -> 17919 bytes .../test_images/ducky/test/out11.jpg | Bin 0 -> 17488 bytes .../test_images/ducky/test/out12.jpg | Bin 0 -> 16372 bytes .../test_images/ducky/test/out13.jpg | Bin 0 -> 16194 bytes .../test_images/ducky/test/out14.jpg | Bin 0 -> 16018 bytes .../test_images/ducky/test/out15.jpg | Bin 0 -> 15839 bytes .../test_images/ducky/test/out16.jpg | Bin 0 -> 15302 bytes .../test_images/ducky/test/out17.jpg | Bin 0 -> 14878 bytes .../test_images/ducky/test/out18.jpg | Bin 0 -> 14417 bytes .../test_images/ducky/test/out19.jpg | Bin 0 -> 14034 bytes .../test_images/ducky/test/out2.jpg | Bin 0 -> 78439 bytes .../test_images/ducky/test/out20.jpg | Bin 0 -> 13949 bytes .../test_images/ducky/test/out21.jpg | Bin 0 -> 14027 bytes .../test_images/ducky/test/out22.jpg | Bin 0 -> 14363 bytes .../test_images/ducky/test/out23.jpg | Bin 0 -> 15058 bytes .../test_images/ducky/test/out24.jpg | Bin 0 -> 15767 bytes .../test_images/ducky/test/out25.jpg | Bin 0 -> 16098 bytes .../test_images/ducky/test/out26.jpg | Bin 0 -> 16210 bytes .../test_images/ducky/test/out27.jpg | Bin 0 -> 16889 bytes .../test_images/ducky/test/out28.jpg | Bin 0 -> 17205 bytes .../test_images/ducky/test/out29.jpg | Bin 0 -> 17165 bytes .../test_images/ducky/test/out3.jpg | Bin 0 -> 74889 bytes .../test_images/ducky/test/out30.jpg | Bin 0 -> 17079 bytes .../test_images/ducky/test/out31.jpg | Bin 0 -> 17129 bytes .../test_images/ducky/test/out32.jpg | Bin 0 -> 16808 bytes .../test_images/ducky/test/out33.jpg | Bin 0 -> 16761 bytes .../test_images/ducky/test/out34.jpg | Bin 0 -> 16845 bytes .../test_images/ducky/test/out35.jpg | Bin 0 -> 16735 bytes .../test_images/ducky/test/out36.jpg | Bin 0 -> 17291 bytes .../test_images/ducky/test/out37.jpg | Bin 0 -> 17444 bytes .../test_images/ducky/test/out38.jpg | Bin 0 -> 17610 bytes .../test_images/ducky/test/out39.jpg | Bin 0 -> 17994 bytes .../test_images/ducky/test/out4.jpg | Bin 0 -> 69856 bytes .../test_images/ducky/test/out40.jpg | Bin 0 -> 18058 bytes .../test_images/ducky/test/out41.jpg | Bin 0 -> 17605 bytes .../test_images/ducky/test/out42.jpg | Bin 0 -> 18462 bytes .../test_images/ducky/test/out43.jpg | Bin 0 -> 18223 bytes .../test_images/ducky/test/out44.jpg | Bin 0 -> 18322 bytes .../test_images/ducky/test/out45.jpg | Bin 0 -> 18534 bytes .../test_images/ducky/test/out46.jpg | Bin 0 -> 18847 bytes .../test_images/ducky/test/out47.jpg | Bin 0 -> 18941 bytes .../test_images/ducky/test/out48.jpg | Bin 0 -> 19087 bytes .../test_images/ducky/test/out49.jpg | Bin 0 -> 19452 bytes .../test_images/ducky/test/out5.jpg | Bin 0 -> 32330 bytes .../test_images/ducky/test/out6.jpg | Bin 0 -> 21761 bytes .../test_images/ducky/test/out7.jpg | Bin 0 -> 20300 bytes .../test_images/ducky/test/out8.jpg | Bin 0 -> 19113 bytes .../test_images/ducky/test/out9.jpg | Bin 0 -> 17941 bytes .../test_images/ducky/train/robertducky1.jpg | Bin 0 -> 245606 bytes .../test_images/ducky/train/robertducky2.jpg | Bin 0 -> 182484 bytes .../test_images/ducky/train/robertducky3.jpg | Bin 0 -> 375409 bytes .../test_images/ducky/train/robertducky4.jpg | Bin 0 -> 136703 bytes .../test_images/ducky/train/robertducky5.jpg | Bin 0 -> 150327 bytes .../object_detection/test_images/image1.jpg | Bin 0 -> 129862 bytes .../object_detection/test_images/image2.jpg | Bin 0 -> 1415684 bytes .../test_images/image_info.txt | 6 + .../test_images/snapshot_serengeti/README.md | 17 + .../S1_E03_R3_PICT0038.jpeg | Bin 0 -> 630625 bytes .../S1_E03_R3_PICT0039.jpeg | Bin 0 -> 631194 bytes .../S1_E03_R3_PICT0040.jpeg | Bin 0 -> 629981 bytes .../S1_E03_R3_PICT0041.jpeg | Bin 0 -> 641490 bytes .../context_rcnn_demo_metadata.json | 1 + .../tpu_exporters/__init__.py | 1 + .../tpu_exporters/export_saved_model_tpu.py | 54 + .../export_saved_model_tpu_lib.py | 216 + .../export_saved_model_tpu_lib_tf1_test.py | 71 + .../tpu_exporters/faster_rcnn.py | 226 + .../object_detection/tpu_exporters/ssd.py | 221 + .../tpu_exporters/testdata/__init__.py | 1 + .../faster_rcnn_resnet101_atrous_coco.config | 80 + .../testdata/ssd/ssd_pipeline.config | 130 + .../object_detection/tpu_exporters/utils.py | 50 + .../tpu_exporters/utils_test.py | 57 + .../object_detection/utils/__init__.py | 0 .../object_detection/utils/__init__.pyc | Bin 0 -> 147 bytes .../utils/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 120 bytes .../autoaugment_utils.cpython-36.pyc | Bin 0 -> 51091 bytes .../__pycache__/config_util.cpython-36.pyc | Bin 0 -> 33894 bytes .../context_manager.cpython-36.pyc | Bin 0 -> 1032 bytes .../__pycache__/dataset_util.cpython-36.pyc | Bin 0 -> 2661 bytes .../__pycache__/json_utils.cpython-36.pyc | Bin 0 -> 2399 bytes .../__pycache__/label_map_util.cpython-36.pyc | Bin 0 -> 10534 bytes .../learning_schedules.cpython-36.pyc | Bin 0 -> 7935 bytes .../utils/__pycache__/metrics.cpython-36.pyc | Bin 0 -> 6237 bytes .../__pycache__/np_box_list.cpython-36.pyc | Bin 0 -> 4780 bytes .../np_box_list_ops.cpython-36.pyc | Bin 0 -> 18117 bytes .../np_box_mask_list.cpython-36.pyc | Bin 0 -> 2230 bytes .../np_box_mask_list_ops.cpython-36.pyc | Bin 0 -> 13123 bytes .../__pycache__/np_box_ops.cpython-36.pyc | Bin 0 -> 2861 bytes .../__pycache__/np_mask_ops.cpython-36.pyc | Bin 0 -> 3866 bytes ...object_detection_evaluation.cpython-36.pyc | Bin 0 -> 46037 bytes .../utils/__pycache__/ops.cpython-36.pyc | Bin 0 -> 41689 bytes .../__pycache__/patch_ops.cpython-36.pyc | Bin 0 -> 2656 bytes .../per_image_evaluation.cpython-36.pyc | Bin 0 -> 22996 bytes .../__pycache__/shape_utils.cpython-36.pyc | Bin 0 -> 17526 bytes .../spatial_transform_ops.cpython-36.pyc | Bin 0 -> 21650 bytes .../__pycache__/static_shape.cpython-36.pyc | Bin 0 -> 1967 bytes .../target_assigner_utils.cpython-36.pyc | Bin 0 -> 14254 bytes .../__pycache__/tf_version.cpython-36.pyc | Bin 0 -> 553 bytes .../variables_helper.cpython-36.pyc | Bin 0 -> 6005 bytes .../visualization_utils.cpython-36.pyc | Bin 0 -> 49374 bytes .../utils/autoaugment_utils.py | 1650 ++ .../utils/autoaugment_utils.pyc | Bin 0 -> 63505 bytes .../object_detection/utils/bifpn_utils.py | 358 + .../object_detection/utils/category_util.py | 77 + .../utils/category_util_test.py | 59 + .../object_detection/utils/colab_utils.py | 480 + .../object_detection/utils/config_util.py | 1075 ++ .../object_detection/utils/config_util.pyc | Bin 0 -> 38388 bytes .../utils/config_util_test.py | 984 ++ .../object_detection/utils/context_manager.py | 39 + .../utils/context_manager.pyc | Bin 0 -> 1224 bytes .../utils/context_manager_test.py | 33 + .../object_detection/utils/dataset_util.py | 90 + .../utils/dataset_util_test.py | 41 + .../object_detection/utils/json_utils.py | 82 + .../object_detection/utils/json_utils_test.py | 97 + .../object_detection/utils/label_map_util.py | 333 + .../object_detection/utils/label_map_util.pyc | Bin 0 -> 12155 bytes .../utils/label_map_util_test.py | 499 + .../utils/learning_schedules.py | 218 + .../utils/learning_schedules.pyc | Bin 0 -> 8292 bytes .../utils/learning_schedules_test.py | 161 + .../object_detection/utils/metrics.py | 193 + .../object_detection/utils/metrics_test.py | 147 + .../object_detection/utils/model_util.py | 96 + .../utils/model_util_tf2_test.py | 61 + .../object_detection/utils/np_box_list.py | 137 + .../object_detection/utils/np_box_list_ops.py | 563 + .../utils/np_box_list_ops_test.py | 418 + .../utils/np_box_list_test.py | 139 + .../utils/np_box_mask_list.py | 66 + .../utils/np_box_mask_list_ops.py | 404 + .../utils/np_box_mask_list_ops_test.py | 195 + .../utils/np_box_mask_list_test.py | 186 + .../object_detection/utils/np_box_ops.py | 102 + .../object_detection/utils/np_box_ops_test.py | 72 + .../object_detection/utils/np_mask_ops.py | 124 + .../utils/np_mask_ops_test.py | 92 + .../utils/object_detection_evaluation.py | 1342 ++ .../utils/object_detection_evaluation_test.py | 1115 ++ .../virtuallab/object_detection/utils/ops.py | 1197 ++ .../virtuallab/object_detection/utils/ops.pyc | Bin 0 -> 46019 bytes .../object_detection/utils/ops_test.py | 1753 ++ .../object_detection/utils/patch_ops.py | 85 + .../object_detection/utils/patch_ops.pyc | Bin 0 -> 2994 bytes .../object_detection/utils/patch_ops_test.py | 149 + .../utils/per_image_evaluation.py | 706 + .../utils/per_image_evaluation_test.py | 718 + .../utils/per_image_vrd_evaluation.py | 229 + .../utils/per_image_vrd_evaluation_test.py | 99 + .../object_detection/utils/shape_utils.py | 499 + .../object_detection/utils/shape_utils.pyc | Bin 0 -> 18168 bytes .../utils/shape_utils_test.py | 452 + .../utils/spatial_transform_ops.py | 584 + .../utils/spatial_transform_ops.pyc | Bin 0 -> 23163 bytes .../utils/spatial_transform_ops_test.py | 603 + .../object_detection/utils/static_shape.py | 90 + .../object_detection/utils/static_shape.pyc | Bin 0 -> 2354 bytes .../utils/static_shape_test.py | 54 + .../utils/target_assigner_utils.py | 410 + .../utils/target_assigner_utils.pyc | Bin 0 -> 16016 bytes .../utils/target_assigner_utils_test.py | 247 + .../object_detection/utils/test_case.py | 285 + .../object_detection/utils/test_case_test.py | 73 + .../object_detection/utils/test_utils.py | 289 + .../object_detection/utils/test_utils_test.py | 94 + .../object_detection/utils/tf_version.py | 27 + .../object_detection/utils/tf_version.pyc | Bin 0 -> 692 bytes .../utils/variables_helper.py | 178 + .../utils/variables_helper.pyc | Bin 0 -> 6373 bytes .../utils/variables_helper_tf1_test.py | 250 + .../utils/visualization_utils.py | 1498 ++ .../utils/visualization_utils.pyc | Bin 0 -> 55092 bytes .../utils/visualization_utils_test.py | 618 + .../object_detection/utils/vrd_evaluation.py | 588 + .../utils/vrd_evaluation_test.py | 261 + 1285 files changed, 230387 insertions(+) create mode 100644 workspace/virtuallab/object_detection/CONTRIBUTING.md create mode 100644 workspace/virtuallab/object_detection/README.md create mode 100644 workspace/virtuallab/object_detection/__init__.py create mode 100644 workspace/virtuallab/object_detection/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/__pycache__/eval_util.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/__pycache__/model_lib.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__init__.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__pycache__/flexible_grid_anchor_generator.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__pycache__/grid_anchor_generator.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__pycache__/multiple_grid_anchor_generator.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/__pycache__/multiscale_grid_anchor_generator.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator_test.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator_test.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator_test.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/multiscale_grid_anchor_generator.py create mode 100644 workspace/virtuallab/object_detection/anchor_generators/multiscale_grid_anchor_generator.pyc create mode 100644 workspace/virtuallab/object_detection/anchor_generators/multiscale_grid_anchor_generator_test.py create mode 100644 workspace/virtuallab/object_detection/box_coders/__init__.py create mode 100644 workspace/virtuallab/object_detection/box_coders/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/__pycache__/faster_rcnn_box_coder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/__pycache__/keypoint_box_coder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/__pycache__/mean_stddev_box_coder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/__pycache__/square_box_coder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.py create mode 100644 workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder_test.py create mode 100644 workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.py create mode 100644 workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/keypoint_box_coder_test.py create mode 100644 workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.py create mode 100644 workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder_test.py create mode 100644 workspace/virtuallab/object_detection/box_coders/square_box_coder.py create mode 100644 workspace/virtuallab/object_detection/box_coders/square_box_coder.pyc create mode 100644 workspace/virtuallab/object_detection/box_coders/square_box_coder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/__init__.py create mode 100644 workspace/virtuallab/object_detection/builders/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/anchor_generator_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/box_coder_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/box_predictor_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/calibration_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/dataset_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/decoder_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/graph_rewriter_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/hyperparams_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/image_resizer_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/losses_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/matcher_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/model_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/optimizer_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/post_processing_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/preprocessor_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/__pycache__/region_similarity_calculator_builder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/builders/anchor_generator_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/anchor_generator_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/anchor_generator_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/box_coder_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/box_coder_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/box_coder_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/box_predictor_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/box_predictor_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/box_predictor_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/calibration_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/calibration_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/calibration_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/dataset_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/dataset_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/dataset_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/decoder_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/decoder_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/decoder_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/graph_rewriter_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/graph_rewriter_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/graph_rewriter_builder_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/builders/hyperparams_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/hyperparams_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/hyperparams_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/image_resizer_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/image_resizer_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/image_resizer_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/input_reader_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/input_reader_builder_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/builders/losses_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/losses_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/losses_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/matcher_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/matcher_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/matcher_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/model_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/model_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/model_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/model_builder_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/builders/model_builder_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/builders/optimizer_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/optimizer_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/optimizer_builder_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/builders/optimizer_builder_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/builders/post_processing_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/post_processing_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/post_processing_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/preprocessor_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/preprocessor_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/preprocessor_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.pyc create mode 100644 workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder_test.py create mode 100644 workspace/virtuallab/object_detection/builders/target_assigner_builder.py create mode 100644 workspace/virtuallab/object_detection/builders/target_assigner_builder_test.py create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/.ipynb_checkpoints/object_detection_tutorial-checkpoint.ipynb create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/context_rcnn_tutorial.ipynb create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tf2_colab.ipynb create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tflite.ipynb create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/inference_from_saved_model_tf2_colab.ipynb create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/inference_tf2_colab.ipynb create mode 100644 workspace/virtuallab/object_detection/colab_tutorials/object_detection_tutorial.ipynb create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_kpts_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_kpts_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_resnet101_v1_fpn_512x512_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v1_fpn_512x512_kpts_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v2_512x512_kpts_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_1024x1024_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_800x1333_coco17_gpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_1024x1024_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_800x1333_coco17_gpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_1024x1024_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_800x1333_coco17_gpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_fpn_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/mask_rcnn_inception_resnet_v2_1024x1024_coco17_gpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d0_512x512_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d1_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d2_768x768_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d3_896x896_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d4_1024x1024_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d5_1280x1280_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d6_1408x1408_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d7_1536x1536_coco17_tpu-32.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v1_fpn_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_320x320_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_1024x1024_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_1024x1024_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_1024x1024_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.config create mode 100644 workspace/virtuallab/object_detection/core/__init__.py create mode 100644 workspace/virtuallab/object_detection/core/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/anchor_generator.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/balanced_positive_negative_sampler.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/batcher.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/box_coder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/box_list.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/box_list_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/data_decoder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/densepose_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/freezable_batch_norm.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/keypoint_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/losses.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/matcher.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/minibatch_sampler.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/model.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/post_processing.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/prefetcher.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/preprocessor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/preprocessor_cache.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/region_similarity_calculator.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/standard_fields.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/__pycache__/target_assigner.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/core/anchor_generator.py create mode 100644 workspace/virtuallab/object_detection/core/anchor_generator.pyc create mode 100644 workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler.py create mode 100644 workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler.pyc create mode 100644 workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler_test.py create mode 100644 workspace/virtuallab/object_detection/core/batch_multiclass_nms_test.py create mode 100644 workspace/virtuallab/object_detection/core/batcher.py create mode 100644 workspace/virtuallab/object_detection/core/batcher.pyc create mode 100644 workspace/virtuallab/object_detection/core/batcher_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/core/box_coder.py create mode 100644 workspace/virtuallab/object_detection/core/box_coder.pyc create mode 100644 workspace/virtuallab/object_detection/core/box_coder_test.py create mode 100644 workspace/virtuallab/object_detection/core/box_list.py create mode 100644 workspace/virtuallab/object_detection/core/box_list.pyc create mode 100644 workspace/virtuallab/object_detection/core/box_list_ops.py create mode 100644 workspace/virtuallab/object_detection/core/box_list_ops.pyc create mode 100644 workspace/virtuallab/object_detection/core/box_list_ops_test.py create mode 100644 workspace/virtuallab/object_detection/core/box_list_test.py create mode 100644 workspace/virtuallab/object_detection/core/box_predictor.py create mode 100644 workspace/virtuallab/object_detection/core/box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/core/class_agnostic_nms_test.py create mode 100644 workspace/virtuallab/object_detection/core/data_decoder.py create mode 100644 workspace/virtuallab/object_detection/core/data_decoder.pyc create mode 100644 workspace/virtuallab/object_detection/core/data_parser.py create mode 100644 workspace/virtuallab/object_detection/core/densepose_ops.py create mode 100644 workspace/virtuallab/object_detection/core/densepose_ops.pyc create mode 100644 workspace/virtuallab/object_detection/core/densepose_ops_test.py create mode 100644 workspace/virtuallab/object_detection/core/freezable_batch_norm.py create mode 100644 workspace/virtuallab/object_detection/core/freezable_batch_norm.pyc create mode 100644 workspace/virtuallab/object_detection/core/freezable_batch_norm_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/core/keypoint_ops.py create mode 100644 workspace/virtuallab/object_detection/core/keypoint_ops.pyc create mode 100644 workspace/virtuallab/object_detection/core/keypoint_ops_test.py create mode 100644 workspace/virtuallab/object_detection/core/losses.py create mode 100644 workspace/virtuallab/object_detection/core/losses.pyc create mode 100644 workspace/virtuallab/object_detection/core/losses_test.py create mode 100644 workspace/virtuallab/object_detection/core/matcher.py create mode 100644 workspace/virtuallab/object_detection/core/matcher.pyc create mode 100644 workspace/virtuallab/object_detection/core/matcher_test.py create mode 100644 workspace/virtuallab/object_detection/core/minibatch_sampler.py create mode 100644 workspace/virtuallab/object_detection/core/minibatch_sampler.pyc create mode 100644 workspace/virtuallab/object_detection/core/minibatch_sampler_test.py create mode 100644 workspace/virtuallab/object_detection/core/model.py create mode 100644 workspace/virtuallab/object_detection/core/model.pyc create mode 100644 workspace/virtuallab/object_detection/core/model_test.py create mode 100644 workspace/virtuallab/object_detection/core/multiclass_nms_test.py create mode 100644 workspace/virtuallab/object_detection/core/post_processing.py create mode 100644 workspace/virtuallab/object_detection/core/post_processing.pyc create mode 100644 workspace/virtuallab/object_detection/core/prefetcher.py create mode 100644 workspace/virtuallab/object_detection/core/prefetcher.pyc create mode 100644 workspace/virtuallab/object_detection/core/prefetcher_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/core/preprocessor.py create mode 100644 workspace/virtuallab/object_detection/core/preprocessor.pyc create mode 100644 workspace/virtuallab/object_detection/core/preprocessor_cache.py create mode 100644 workspace/virtuallab/object_detection/core/preprocessor_cache.pyc create mode 100644 workspace/virtuallab/object_detection/core/preprocessor_test.py create mode 100644 workspace/virtuallab/object_detection/core/region_similarity_calculator.py create mode 100644 workspace/virtuallab/object_detection/core/region_similarity_calculator.pyc create mode 100644 workspace/virtuallab/object_detection/core/region_similarity_calculator_test.py create mode 100644 workspace/virtuallab/object_detection/core/standard_fields.py create mode 100644 workspace/virtuallab/object_detection/core/standard_fields.pyc create mode 100644 workspace/virtuallab/object_detection/core/target_assigner.py create mode 100644 workspace/virtuallab/object_detection/core/target_assigner.pyc create mode 100644 workspace/virtuallab/object_detection/core/target_assigner_test.py create mode 100644 workspace/virtuallab/object_detection/data/ava_label_map_v2.1.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/face_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/face_person_with_keypoints_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/fgvc_2854_classes_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/kitti_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/mscoco_complete_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/mscoco_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/mscoco_minival_ids.txt create mode 100644 workspace/virtuallab/object_detection/data/oid_bbox_trainable_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/oid_object_detection_challenge_500_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/oid_v4_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/pascal_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/pet_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/snapshot_serengeti_label_map.pbtxt create mode 100644 workspace/virtuallab/object_detection/data/test_labels.csv create mode 100644 workspace/virtuallab/object_detection/data/train_labels.csv create mode 100644 workspace/virtuallab/object_detection/data_decoders/__init__.py create mode 100644 workspace/virtuallab/object_detection/data_decoders/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/data_decoders/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/data_decoders/__pycache__/tf_example_decoder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/data_decoders/__pycache__/tf_sequence_example_decoder.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.py create mode 100644 workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.pyc create mode 100644 workspace/virtuallab/object_detection/data_decoders/tf_example_decoder_test.py create mode 100644 workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.py create mode 100644 workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.pyc create mode 100644 workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/__init__.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/__init__.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_main.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_ava_actions_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_oid_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_pet_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_pycocotools_package.sh create mode 100644 workspace/virtuallab/object_detection/dataset_tools/create_tf_record.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/densepose/UV_symmetry_transforms.mat create mode 100755 workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_ava.sh create mode 100644 workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_mscoco.sh create mode 100644 workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/seq_example_util.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/seq_example_util_test.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util.py create mode 100644 workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util_test.py create mode 100644 workspace/virtuallab/object_detection/dockerfiles/android/Dockerfile create mode 100644 workspace/virtuallab/object_detection/dockerfiles/android/README.md create mode 100644 workspace/virtuallab/object_detection/dockerfiles/tf1/Dockerfile create mode 100644 workspace/virtuallab/object_detection/dockerfiles/tf1/README.md create mode 100644 workspace/virtuallab/object_detection/dockerfiles/tf2/Dockerfile create mode 100644 workspace/virtuallab/object_detection/dockerfiles/tf2/README.md create mode 100644 workspace/virtuallab/object_detection/eval_util.py create mode 100644 workspace/virtuallab/object_detection/eval_util_test.py create mode 100644 workspace/virtuallab/object_detection/export_inference_graph.py create mode 100644 workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2.py create mode 100644 workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/export_tflite_graph_tf2.py create mode 100644 workspace/virtuallab/object_detection/export_tflite_ssd_graph.py create mode 100644 workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib.py create mode 100644 workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/exporter.py create mode 100644 workspace/virtuallab/object_detection/exporter_lib_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/exporter_lib_v2.py create mode 100644 workspace/virtuallab/object_detection/exporter_main_v2.py create mode 100644 workspace/virtuallab/object_detection/exporter_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/g3doc/challenge_evaluation.md create mode 100644 workspace/virtuallab/object_detection/g3doc/configuring_jobs.md create mode 100644 workspace/virtuallab/object_detection/g3doc/context_rcnn.md create mode 100644 workspace/virtuallab/object_detection/g3doc/defining_your_own_model.md create mode 100644 workspace/virtuallab/object_detection/g3doc/evaluation_protocols.md create mode 100644 workspace/virtuallab/object_detection/g3doc/exporting_models.md create mode 100644 workspace/virtuallab/object_detection/g3doc/faq.md create mode 100644 workspace/virtuallab/object_detection/g3doc/img/dogs_detections_output.jpg create mode 100644 workspace/virtuallab/object_detection/g3doc/img/example_cat.jpg create mode 100644 workspace/virtuallab/object_detection/g3doc/img/groupof_case_eval.png create mode 100644 workspace/virtuallab/object_detection/g3doc/img/kites_detections_output.jpg create mode 100644 workspace/virtuallab/object_detection/g3doc/img/kites_with_segment_overlay.png create mode 100644 workspace/virtuallab/object_detection/g3doc/img/nongroupof_case_eval.png create mode 100644 workspace/virtuallab/object_detection/g3doc/img/oid_bus_72e19c28aac34ed8.jpg create mode 100644 workspace/virtuallab/object_detection/g3doc/img/oid_monkey_3b4168c89cecbc5b.jpg create mode 100644 workspace/virtuallab/object_detection/g3doc/img/oxford_pet.png create mode 100644 workspace/virtuallab/object_detection/g3doc/img/tensorboard.png create mode 100644 workspace/virtuallab/object_detection/g3doc/img/tensorboard2.png create mode 100644 workspace/virtuallab/object_detection/g3doc/img/tf-od-api-logo.png create mode 100644 workspace/virtuallab/object_detection/g3doc/instance_segmentation.md create mode 100644 workspace/virtuallab/object_detection/g3doc/oid_inference_and_evaluation.md create mode 100644 workspace/virtuallab/object_detection/g3doc/preparing_inputs.md create mode 100644 workspace/virtuallab/object_detection/g3doc/release_notes.md create mode 100644 workspace/virtuallab/object_detection/g3doc/running_notebook.md create mode 100644 workspace/virtuallab/object_detection/g3doc/running_on_mobile_tensorflowlite.md create mode 100644 workspace/virtuallab/object_detection/g3doc/running_on_mobile_tf2.md create mode 100644 workspace/virtuallab/object_detection/g3doc/running_pets.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf1.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf1_detection_zoo.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf1_training_and_evaluation.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf2.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf2_classification_zoo.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf2_detection_zoo.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tf2_training_and_evaluation.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tpu_compatibility.md create mode 100644 workspace/virtuallab/object_detection/g3doc/tpu_exporters.md create mode 100644 workspace/virtuallab/object_detection/g3doc/using_your_own_dataset.md create mode 100644 workspace/virtuallab/object_detection/generate_tfrecord.py create mode 100644 workspace/virtuallab/object_detection/images/test/frame1000.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame1000.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame1035.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame1035.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame1040.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame1040.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame1045.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame1045.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame1050.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame1050.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame2025.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame2025.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame2035.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame2035.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame2040.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame2040.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame2045.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame2045.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame2050.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame2050.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame2051.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame2051.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame3035.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame3035.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame3040.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame3040.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame3045.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame3045.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame3050.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame3050.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame4025.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame4025.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame4035.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame4035.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame4040.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame4040.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame4045.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame4045.xml create mode 100644 workspace/virtuallab/object_detection/images/test/frame4050.jpg create mode 100644 workspace/virtuallab/object_detection/images/test/frame4050.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1024.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1024.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1025.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1025.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1026.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1026.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1027.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1027.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1028.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1028.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1029.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1029.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1030.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1030.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1031.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1031.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1032.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1032.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1033.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1033.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1034.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1034.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1036.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1036.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1037.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1037.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1038.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1038.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1039.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1039.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1041.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1041.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1042.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1042.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1043.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1043.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1044.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1044.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1046.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1046.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1047.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1047.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1048.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1048.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1049.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1049.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame1051.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame1051.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2000.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2000.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2024.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2024.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2026.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2026.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2027.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2027.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2028.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2028.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2029.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2029.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2030.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2030.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2031.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2031.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2032.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2032.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2033.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2033.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2034.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2034.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2036.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2036.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2037.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2037.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2038.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2038.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2039.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2039.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2041.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2041.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2042.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2042.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2043.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2043.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2044.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2044.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2046.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2046.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2047.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2047.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2048.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2048.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame2049.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame2049.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3000.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3000.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3024.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3024.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3025.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3025.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3026.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3026.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3027.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3027.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3028.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3028.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3029.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3029.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3030.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3030.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3031.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3031.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3032.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3032.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3033.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3033.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3034.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3034.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3036.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3036.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3037.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3037.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3038.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3038.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3039.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3039.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3041.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3041.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3042.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3042.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3043.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3043.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3044.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3044.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3046.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3046.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3047.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3047.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3048.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3048.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3049.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3049.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame3051.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame3051.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4000.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4000.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4026.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4026.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4027.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4027.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4028.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4028.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4029.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4029.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4030.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4030.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4031.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4031.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4032.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4032.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4033.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4033.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4034.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4034.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4036.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4036.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4037.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4037.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4038.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4038.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4039.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4039.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4041.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4041.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4042.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4042.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4043.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4043.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4044.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4044.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4046.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4046.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4047.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4047.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4048.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4048.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4049.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4049.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4051.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4051.xml create mode 100644 workspace/virtuallab/object_detection/images/train/frame4052.jpg create mode 100644 workspace/virtuallab/object_detection/images/train/frame4052.xml create mode 100644 workspace/virtuallab/object_detection/inference/__init__.py create mode 100644 workspace/virtuallab/object_detection/inference/detection_inference.py create mode 100644 workspace/virtuallab/object_detection/inference/detection_inference_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/inference/infer_detections.py create mode 100644 workspace/virtuallab/object_detection/inputs.py create mode 100644 workspace/virtuallab/object_detection/inputs_test.py create mode 100644 workspace/virtuallab/object_detection/legacy/__init__.py create mode 100644 workspace/virtuallab/object_detection/legacy/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/legacy/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/legacy/__pycache__/trainer.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/legacy/eval.py create mode 100644 workspace/virtuallab/object_detection/legacy/evaluator.py create mode 100644 workspace/virtuallab/object_detection/legacy/train.py create mode 100644 workspace/virtuallab/object_detection/legacy/trainer.py create mode 100644 workspace/virtuallab/object_detection/legacy/trainer.pyc create mode 100644 workspace/virtuallab/object_detection/legacy/trainer_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/matchers/__init__.py create mode 100644 workspace/virtuallab/object_detection/matchers/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/__pycache__/argmax_matcher.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/__pycache__/bipartite_matcher.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/__pycache__/hungarian_matcher.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/argmax_matcher.py create mode 100644 workspace/virtuallab/object_detection/matchers/argmax_matcher.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/argmax_matcher_test.py create mode 100644 workspace/virtuallab/object_detection/matchers/bipartite_matcher.py create mode 100644 workspace/virtuallab/object_detection/matchers/bipartite_matcher.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/bipartite_matcher_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/matchers/hungarian_matcher.py create mode 100644 workspace/virtuallab/object_detection/matchers/hungarian_matcher.pyc create mode 100644 workspace/virtuallab/object_detection/matchers/hungarian_matcher_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__init__.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/center_net_meta_arch.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/context_rcnn_lib.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/context_rcnn_lib_tf2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/context_rcnn_meta_arch.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/faster_rcnn_meta_arch.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/rfcn_meta_arch.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/__pycache__/ssd_meta_arch.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/center_net_meta_arch.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/center_net_meta_arch.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/center_net_meta_arch_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_lib.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_lib.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_lib_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_lib_tf2.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_lib_tf2.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_lib_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_meta_arch.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_meta_arch.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/context_rcnn_meta_arch_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/faster_rcnn_meta_arch.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/faster_rcnn_meta_arch.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/faster_rcnn_meta_arch_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/faster_rcnn_meta_arch_test_lib.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/rfcn_meta_arch.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/rfcn_meta_arch.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/rfcn_meta_arch_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/ssd_meta_arch.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/ssd_meta_arch.pyc create mode 100644 workspace/virtuallab/object_detection/meta_architectures/ssd_meta_arch_test.py create mode 100644 workspace/virtuallab/object_detection/meta_architectures/ssd_meta_arch_test_lib.py create mode 100644 workspace/virtuallab/object_detection/metrics/__init__.py create mode 100644 workspace/virtuallab/object_detection/metrics/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/metrics/__pycache__/coco_evaluation.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/metrics/__pycache__/coco_tools.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/metrics/__pycache__/lvis_evaluation.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/metrics/calibration_evaluation.py create mode 100644 workspace/virtuallab/object_detection/metrics/calibration_evaluation_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/calibration_metrics.py create mode 100644 workspace/virtuallab/object_detection/metrics/calibration_metrics_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/coco_evaluation.py create mode 100644 workspace/virtuallab/object_detection/metrics/coco_evaluation_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/coco_tools.py create mode 100644 workspace/virtuallab/object_detection/metrics/coco_tools_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/io_utils.py create mode 100644 workspace/virtuallab/object_detection/metrics/lvis_evaluation.py create mode 100644 workspace/virtuallab/object_detection/metrics/lvis_evaluation_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/lvis_tools.py create mode 100644 workspace/virtuallab/object_detection/metrics/lvis_tools_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/offline_eval_map_corloc.py create mode 100644 workspace/virtuallab/object_detection/metrics/offline_eval_map_corloc_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/oid_challenge_evaluation.py create mode 100644 workspace/virtuallab/object_detection/metrics/oid_challenge_evaluation_utils.py create mode 100644 workspace/virtuallab/object_detection/metrics/oid_challenge_evaluation_utils_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/oid_vrd_challenge_evaluation.py create mode 100644 workspace/virtuallab/object_detection/metrics/oid_vrd_challenge_evaluation_utils.py create mode 100644 workspace/virtuallab/object_detection/metrics/oid_vrd_challenge_evaluation_utils_test.py create mode 100644 workspace/virtuallab/object_detection/metrics/tf_example_parser.py create mode 100644 workspace/virtuallab/object_detection/metrics/tf_example_parser_test.py create mode 100644 workspace/virtuallab/object_detection/model_hparams.py create mode 100644 workspace/virtuallab/object_detection/model_lib.py create mode 100644 workspace/virtuallab/object_detection/model_lib_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/model_lib_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/model_lib_v2.py create mode 100644 workspace/virtuallab/object_detection/model_main.py create mode 100644 workspace/virtuallab/object_detection/model_main_tf2.py create mode 100644 workspace/virtuallab/object_detection/model_tpu_main.py create mode 100644 workspace/virtuallab/object_detection/models/__init__.py create mode 100644 workspace/virtuallab/object_detection/models/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/embedded_ssd_mobilenet_v1_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/faster_rcnn_inception_resnet_v2_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/faster_rcnn_inception_v2_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/faster_rcnn_nas_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/faster_rcnn_pnas_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/faster_rcnn_resnet_v1_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/feature_map_generators.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_inception_v2_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_inception_v3_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobiledet_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_edgetpu_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v1_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v1_fpn_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v1_ppn_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v2_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v2_fpn_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v2_mnasfpn_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_mobilenet_v3_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_pnasnet_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_resnet_v1_fpn_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/__pycache__/ssd_resnet_v1_ppn_feature_extractor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/models/bidirectional_feature_pyramid_generators.py create mode 100644 workspace/virtuallab/object_detection/models/bidirectional_feature_pyramid_generators_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_hourglass_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_hourglass_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_mobilenet_v2_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_mobilenet_v2_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_mobilenet_v2_fpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_mobilenet_v2_fpn_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_resnet_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_resnet_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_resnet_v1_fpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/center_net_resnet_v1_fpn_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/embedded_ssd_mobilenet_v1_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/embedded_ssd_mobilenet_v1_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/embedded_ssd_mobilenet_v1_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_resnet_v2_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_resnet_v2_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_resnet_v2_keras_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_v2_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_v2_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_inception_v2_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_mobilenet_v1_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_mobilenet_v1_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_nas_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_nas_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_nas_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_pnas_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_pnas_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_pnas_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_keras_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_v1_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_v1_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_v1_fpn_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/faster_rcnn_resnet_v1_fpn_keras_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/feature_map_generators.py create mode 100644 workspace/virtuallab/object_detection/models/feature_map_generators.pyc create mode 100644 workspace/virtuallab/object_detection/models/feature_map_generators_test.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/__init__.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/base_models/original_mobilenet_v2.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/convert_keras_models.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/hourglass_network.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/hourglass_network_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/inception_resnet_v2.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/inception_resnet_v2_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/mobilenet_v1.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/mobilenet_v1_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/mobilenet_v2.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/mobilenet_v2_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/model_utils.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/resnet_v1.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/resnet_v1_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/keras_models/test_utils.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_efficientnet_bifpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_efficientnet_bifpn_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_feature_extractor_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_inception_v2_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_inception_v2_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_inception_v2_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_inception_v3_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_inception_v3_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_inception_v3_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobiledet_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobiledet_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobiledet_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_edgetpu_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_edgetpu_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_edgetpu_feature_extractor_testbase.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_edgetpu_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_fpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_fpn_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_fpn_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_fpn_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_fpn_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_ppn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_ppn_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v1_ppn_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_fpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_fpn_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_fpn_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_fpn_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_fpn_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_mnasfpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_mnasfpn_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v2_mnasfpn_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v3_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v3_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v3_feature_extractor_testbase.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_mobilenet_v3_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_pnasnet_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_pnasnet_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_pnasnet_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_fpn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_fpn_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_fpn_feature_extractor_testbase.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_fpn_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_fpn_feature_extractor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_fpn_keras_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_ppn_feature_extractor.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_ppn_feature_extractor.pyc create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_ppn_feature_extractor_testbase.py create mode 100644 workspace/virtuallab/object_detection/models/ssd_resnet_v1_ppn_feature_extractor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/packages/tf1/setup.py create mode 100644 workspace/virtuallab/object_detection/packages/tf2/setup.py create mode 100644 workspace/virtuallab/object_detection/predictors/__init__.py create mode 100644 workspace/virtuallab/object_detection/predictors/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/convolutional_box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/convolutional_keras_box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/mask_rcnn_box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/mask_rcnn_keras_box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/rfcn_box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/__pycache__/rfcn_keras_box_predictor.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/convolutional_box_predictor.py create mode 100644 workspace/virtuallab/object_detection/predictors/convolutional_box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/convolutional_box_predictor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/convolutional_keras_box_predictor.py create mode 100644 workspace/virtuallab/object_detection/predictors/convolutional_keras_box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/convolutional_keras_box_predictor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__init__.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/box_head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/class_head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/keras_box_head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/keras_class_head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/keras_mask_head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/__pycache__/mask_head.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/box_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/box_head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/box_head_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/class_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/class_head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/class_head_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_box_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_box_head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_box_head_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_class_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_class_head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_class_head_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_mask_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_mask_head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keras_mask_head_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keypoint_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/keypoint_head_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/mask_head.py create mode 100644 workspace/virtuallab/object_detection/predictors/heads/mask_head.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/heads/mask_head_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/mask_rcnn_box_predictor.py create mode 100644 workspace/virtuallab/object_detection/predictors/mask_rcnn_box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/mask_rcnn_box_predictor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/mask_rcnn_keras_box_predictor.py create mode 100644 workspace/virtuallab/object_detection/predictors/mask_rcnn_keras_box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/mask_rcnn_keras_box_predictor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/rfcn_box_predictor.py create mode 100644 workspace/virtuallab/object_detection/predictors/rfcn_box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/rfcn_box_predictor_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/predictors/rfcn_keras_box_predictor.py create mode 100644 workspace/virtuallab/object_detection/predictors/rfcn_keras_box_predictor.pyc create mode 100644 workspace/virtuallab/object_detection/predictors/rfcn_keras_box_predictor_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/protos/__init__.py create mode 100644 workspace/virtuallab/object_detection/protos/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__init__/py_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/anchor_generator_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/argmax_matcher_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/bipartite_matcher_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/box_coder_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/box_predictor_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/calibration_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/center_net_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/eval_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/faster_rcnn_box_coder_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/faster_rcnn_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/flexible_grid_anchor_generator_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/fpn_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/graph_rewriter_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/grid_anchor_generator_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/hyperparams_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/image_resizer_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/input_reader_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/keypoint_box_coder_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/losses_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/matcher_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/mean_stddev_box_coder_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/model_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/multiscale_anchor_generator_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/optimizer_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/pipeline_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/post_processing_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/preprocessor_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/region_similarity_calculator_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/square_box_coder_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/ssd_anchor_generator_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/ssd_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/string_int_label_map_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/__pycache__/train_pb2.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/protos/anchor_generator.proto create mode 100644 workspace/virtuallab/object_detection/protos/anchor_generator_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/anchor_generator_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/argmax_matcher.proto create mode 100644 workspace/virtuallab/object_detection/protos/argmax_matcher_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/argmax_matcher_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/bipartite_matcher.proto create mode 100644 workspace/virtuallab/object_detection/protos/bipartite_matcher_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/bipartite_matcher_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/box_coder.proto create mode 100644 workspace/virtuallab/object_detection/protos/box_coder_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/box_coder_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/box_predictor.proto create mode 100644 workspace/virtuallab/object_detection/protos/box_predictor_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/box_predictor_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/calibration.proto create mode 100644 workspace/virtuallab/object_detection/protos/calibration_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/calibration_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/center_net.proto create mode 100644 workspace/virtuallab/object_detection/protos/center_net_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/center_net_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/eval.proto create mode 100644 workspace/virtuallab/object_detection/protos/eval_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/eval_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/faster_rcnn.proto create mode 100644 workspace/virtuallab/object_detection/protos/faster_rcnn_box_coder.proto create mode 100644 workspace/virtuallab/object_detection/protos/faster_rcnn_box_coder_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/faster_rcnn_box_coder_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/faster_rcnn_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/faster_rcnn_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/flexible_grid_anchor_generator.proto create mode 100644 workspace/virtuallab/object_detection/protos/flexible_grid_anchor_generator_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/flexible_grid_anchor_generator_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/fpn.proto create mode 100644 workspace/virtuallab/object_detection/protos/fpn_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/fpn_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/graph_rewriter.proto create mode 100644 workspace/virtuallab/object_detection/protos/graph_rewriter_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/graph_rewriter_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/grid_anchor_generator.proto create mode 100644 workspace/virtuallab/object_detection/protos/grid_anchor_generator_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/grid_anchor_generator_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/hyperparams.proto create mode 100644 workspace/virtuallab/object_detection/protos/hyperparams_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/hyperparams_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/image_resizer.proto create mode 100644 workspace/virtuallab/object_detection/protos/image_resizer_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/image_resizer_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/input_reader.proto create mode 100644 workspace/virtuallab/object_detection/protos/input_reader_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/input_reader_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/keypoint_box_coder.proto create mode 100644 workspace/virtuallab/object_detection/protos/keypoint_box_coder_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/keypoint_box_coder_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/losses.proto create mode 100644 workspace/virtuallab/object_detection/protos/losses_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/losses_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/matcher.proto create mode 100644 workspace/virtuallab/object_detection/protos/matcher_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/matcher_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/mean_stddev_box_coder.proto create mode 100644 workspace/virtuallab/object_detection/protos/mean_stddev_box_coder_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/mean_stddev_box_coder_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/model.proto create mode 100644 workspace/virtuallab/object_detection/protos/model_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/model_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/multiscale_anchor_generator.proto create mode 100644 workspace/virtuallab/object_detection/protos/multiscale_anchor_generator_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/multiscale_anchor_generator_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/optimizer.proto create mode 100644 workspace/virtuallab/object_detection/protos/optimizer_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/optimizer_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/pipeline.proto create mode 100644 workspace/virtuallab/object_detection/protos/pipeline_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/pipeline_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/post_processing.proto create mode 100644 workspace/virtuallab/object_detection/protos/post_processing_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/post_processing_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/preprocessor.proto create mode 100644 workspace/virtuallab/object_detection/protos/preprocessor_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/preprocessor_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/region_similarity_calculator.proto create mode 100644 workspace/virtuallab/object_detection/protos/region_similarity_calculator_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/region_similarity_calculator_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/square_box_coder.proto create mode 100644 workspace/virtuallab/object_detection/protos/square_box_coder_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/square_box_coder_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/ssd.proto create mode 100644 workspace/virtuallab/object_detection/protos/ssd_anchor_generator.proto create mode 100644 workspace/virtuallab/object_detection/protos/ssd_anchor_generator_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/ssd_anchor_generator_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/ssd_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/ssd_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/string_int_label_map.proto create mode 100644 workspace/virtuallab/object_detection/protos/string_int_label_map_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/string_int_label_map_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/protos/target_assigner.proto create mode 100644 workspace/virtuallab/object_detection/protos/target_assigner_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/train.proto create mode 100644 workspace/virtuallab/object_detection/protos/train_pb2.py create mode 100644 workspace/virtuallab/object_detection/protos/train_pb2.pyc create mode 100644 workspace/virtuallab/object_detection/samples/cloud/cloud.yml create mode 100644 workspace/virtuallab/object_detection/samples/configs/context_rcnn_resnet101_snapshot_serengeti.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/context_rcnn_resnet101_snapshot_serengeti_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/embedded_ssd_mobilenet_v1_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/facessd_mobilenet_v2_quantized_320x320_open_image_v4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_cosine_lr_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_oid.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_oid_v4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_resnet_v2_atrous_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_v2_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_inception_v2_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_nas_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_atrous_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_ava_v2.1.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_fgvc.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_kitti.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet101_voc07.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet152_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet152_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet50_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet50_fgvc.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/faster_rcnn_resnet50_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/mask_rcnn_inception_resnet_v2_atrous_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/mask_rcnn_inception_v2_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/mask_rcnn_resnet101_atrous_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/mask_rcnn_resnet101_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/mask_rcnn_resnet50_atrous_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/rfcn_resnet101_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/rfcn_resnet101_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_inception_v2_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_inception_v2_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_inception_v3_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_0.75_depth_300x300_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_0.75_depth_quantized_300x300_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_0.75_depth_quantized_300x300_pets_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_300x300_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_focal_loss_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_focal_loss_pets_inference.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_fpn_shared_box_predictor_640x640_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_pets.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_ppn_shared_box_predictor_300x300_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v1_quantized_300x300_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_fpnlite_quantized_shared_box_predictor_256x256_depthmultiplier_75_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_fullyconv_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_mnasfpn_shared_box_predictor_320x320_coco_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_oid_v4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_pets_keras.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_mobilenet_v2_quantized_300x300_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_resnet101_v1_fpn_shared_box_predictor_oid_512x512_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssd_resnet50_v1_fpn_shared_box_predictor_640x640_coco14_sync.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobiledet_cpu_320x320_coco_sync_4x4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobiledet_dsp_320x320_coco_sync_4x4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobiledet_edgetpu_320x320_coco_sync_4x4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobiledet_gpu_320x320_coco_sync_4x4.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobilenet_edgetpu_320x320_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobilenet_edgetpu_320x320_coco_quant.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobilenet_v1_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobilenet_v2_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobilenet_v3_large_320x320_coco.config create mode 100644 workspace/virtuallab/object_detection/samples/configs/ssdlite_mobilenet_v3_small_320x320_coco.config create mode 100644 workspace/virtuallab/object_detection/test_data/context_rcnn_camera_trap.config create mode 100644 workspace/virtuallab/object_detection/test_data/pets_examples.record create mode 100644 workspace/virtuallab/object_detection/test_data/snapshot_serengeti_sequence_examples.record create mode 100644 workspace/virtuallab/object_detection/test_data/ssd_mobilenet_v1_fpp.config create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out1.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out10.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out11.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out12.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out13.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out14.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out15.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out16.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out17.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out18.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out19.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out2.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out20.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out21.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out22.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out23.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out24.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out25.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out26.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out27.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out28.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out29.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out3.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out30.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out31.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out32.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out33.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out34.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out35.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out36.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out37.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out38.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out39.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out4.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out40.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out41.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out42.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out43.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out44.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out45.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out46.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out47.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out48.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out49.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out5.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out6.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out7.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out8.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/test/out9.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/train/robertducky1.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/train/robertducky2.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/train/robertducky3.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/train/robertducky4.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/ducky/train/robertducky5.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/image1.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/image2.jpg create mode 100644 workspace/virtuallab/object_detection/test_images/image_info.txt create mode 100644 workspace/virtuallab/object_detection/test_images/snapshot_serengeti/README.md create mode 100644 workspace/virtuallab/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0038.jpeg create mode 100644 workspace/virtuallab/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0039.jpeg create mode 100644 workspace/virtuallab/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0040.jpeg create mode 100644 workspace/virtuallab/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0041.jpeg create mode 100644 workspace/virtuallab/object_detection/test_images/snapshot_serengeti/context_rcnn_demo_metadata.json create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/__init__.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/export_saved_model_tpu.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/export_saved_model_tpu_lib.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/export_saved_model_tpu_lib_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/faster_rcnn.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/ssd.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/testdata/__init__.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/testdata/faster_rcnn/faster_rcnn_resnet101_atrous_coco.config create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/testdata/ssd/ssd_pipeline.config create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/utils.py create mode 100644 workspace/virtuallab/object_detection/tpu_exporters/utils_test.py create mode 100644 workspace/virtuallab/object_detection/utils/__init__.py create mode 100644 workspace/virtuallab/object_detection/utils/__init__.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/__init__.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/autoaugment_utils.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/config_util.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/context_manager.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/dataset_util.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/json_utils.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/label_map_util.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/learning_schedules.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/metrics.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/np_box_list.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/np_box_list_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/np_box_mask_list.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/np_box_mask_list_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/np_box_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/np_mask_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/object_detection_evaluation.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/patch_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/per_image_evaluation.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/shape_utils.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/spatial_transform_ops.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/static_shape.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/target_assigner_utils.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/tf_version.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/variables_helper.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/__pycache__/visualization_utils.cpython-36.pyc create mode 100644 workspace/virtuallab/object_detection/utils/autoaugment_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/autoaugment_utils.pyc create mode 100644 workspace/virtuallab/object_detection/utils/bifpn_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/category_util.py create mode 100644 workspace/virtuallab/object_detection/utils/category_util_test.py create mode 100644 workspace/virtuallab/object_detection/utils/colab_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/config_util.py create mode 100644 workspace/virtuallab/object_detection/utils/config_util.pyc create mode 100644 workspace/virtuallab/object_detection/utils/config_util_test.py create mode 100644 workspace/virtuallab/object_detection/utils/context_manager.py create mode 100644 workspace/virtuallab/object_detection/utils/context_manager.pyc create mode 100644 workspace/virtuallab/object_detection/utils/context_manager_test.py create mode 100644 workspace/virtuallab/object_detection/utils/dataset_util.py create mode 100644 workspace/virtuallab/object_detection/utils/dataset_util_test.py create mode 100644 workspace/virtuallab/object_detection/utils/json_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/json_utils_test.py create mode 100644 workspace/virtuallab/object_detection/utils/label_map_util.py create mode 100644 workspace/virtuallab/object_detection/utils/label_map_util.pyc create mode 100644 workspace/virtuallab/object_detection/utils/label_map_util_test.py create mode 100644 workspace/virtuallab/object_detection/utils/learning_schedules.py create mode 100644 workspace/virtuallab/object_detection/utils/learning_schedules.pyc create mode 100644 workspace/virtuallab/object_detection/utils/learning_schedules_test.py create mode 100644 workspace/virtuallab/object_detection/utils/metrics.py create mode 100644 workspace/virtuallab/object_detection/utils/metrics_test.py create mode 100644 workspace/virtuallab/object_detection/utils/model_util.py create mode 100644 workspace/virtuallab/object_detection/utils/model_util_tf2_test.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_list.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_list_ops.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_list_ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_list_test.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_mask_list.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_mask_list_ops.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_mask_list_ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_mask_list_test.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_ops.py create mode 100644 workspace/virtuallab/object_detection/utils/np_box_ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/np_mask_ops.py create mode 100644 workspace/virtuallab/object_detection/utils/np_mask_ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/object_detection_evaluation.py create mode 100644 workspace/virtuallab/object_detection/utils/object_detection_evaluation_test.py create mode 100644 workspace/virtuallab/object_detection/utils/ops.py create mode 100644 workspace/virtuallab/object_detection/utils/ops.pyc create mode 100644 workspace/virtuallab/object_detection/utils/ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/patch_ops.py create mode 100644 workspace/virtuallab/object_detection/utils/patch_ops.pyc create mode 100644 workspace/virtuallab/object_detection/utils/patch_ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/per_image_evaluation.py create mode 100644 workspace/virtuallab/object_detection/utils/per_image_evaluation_test.py create mode 100644 workspace/virtuallab/object_detection/utils/per_image_vrd_evaluation.py create mode 100644 workspace/virtuallab/object_detection/utils/per_image_vrd_evaluation_test.py create mode 100644 workspace/virtuallab/object_detection/utils/shape_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/shape_utils.pyc create mode 100644 workspace/virtuallab/object_detection/utils/shape_utils_test.py create mode 100644 workspace/virtuallab/object_detection/utils/spatial_transform_ops.py create mode 100644 workspace/virtuallab/object_detection/utils/spatial_transform_ops.pyc create mode 100644 workspace/virtuallab/object_detection/utils/spatial_transform_ops_test.py create mode 100644 workspace/virtuallab/object_detection/utils/static_shape.py create mode 100644 workspace/virtuallab/object_detection/utils/static_shape.pyc create mode 100644 workspace/virtuallab/object_detection/utils/static_shape_test.py create mode 100644 workspace/virtuallab/object_detection/utils/target_assigner_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/target_assigner_utils.pyc create mode 100644 workspace/virtuallab/object_detection/utils/target_assigner_utils_test.py create mode 100644 workspace/virtuallab/object_detection/utils/test_case.py create mode 100644 workspace/virtuallab/object_detection/utils/test_case_test.py create mode 100644 workspace/virtuallab/object_detection/utils/test_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/test_utils_test.py create mode 100644 workspace/virtuallab/object_detection/utils/tf_version.py create mode 100644 workspace/virtuallab/object_detection/utils/tf_version.pyc create mode 100644 workspace/virtuallab/object_detection/utils/variables_helper.py create mode 100644 workspace/virtuallab/object_detection/utils/variables_helper.pyc create mode 100644 workspace/virtuallab/object_detection/utils/variables_helper_tf1_test.py create mode 100644 workspace/virtuallab/object_detection/utils/visualization_utils.py create mode 100644 workspace/virtuallab/object_detection/utils/visualization_utils.pyc create mode 100644 workspace/virtuallab/object_detection/utils/visualization_utils_test.py create mode 100644 workspace/virtuallab/object_detection/utils/vrd_evaluation.py create mode 100644 workspace/virtuallab/object_detection/utils/vrd_evaluation_test.py diff --git a/workspace/virtuallab/object_detection/CONTRIBUTING.md b/workspace/virtuallab/object_detection/CONTRIBUTING.md new file mode 100644 index 0000000..8073982 --- /dev/null +++ b/workspace/virtuallab/object_detection/CONTRIBUTING.md @@ -0,0 +1,13 @@ +# Contributing to the TensorFlow Object Detection API + +Patches to TensorFlow Object Detection API are welcome! + +We require contributors to fill out either the individual or corporate +Contributor License Agreement (CLA). + + * If you are an individual writing original source code and you're sure you own the intellectual property, then you'll need to sign an [individual CLA](http://code.google.com/legal/individual-cla-v1.0.html). + * If you work for a company that wants to allow you to contribute your work, then you'll need to sign a [corporate CLA](http://code.google.com/legal/corporate-cla-v1.0.html). + +Please follow the +[TensorFlow contributing guidelines](https://github.com/tensorflow/tensorflow/blob/master/CONTRIBUTING.md) +when submitting pull requests. diff --git a/workspace/virtuallab/object_detection/README.md b/workspace/virtuallab/object_detection/README.md new file mode 100644 index 0000000..9327055 --- /dev/null +++ b/workspace/virtuallab/object_detection/README.md @@ -0,0 +1,191 @@ +# TensorFlow Object Detection API +[![TensorFlow 2.2](https://img.shields.io/badge/TensorFlow-2.2-FF6F00?logo=tensorflow)](https://github.com/tensorflow/tensorflow/releases/tag/v2.2.0) +[![TensorFlow 1.15](https://img.shields.io/badge/TensorFlow-1.15-FF6F00?logo=tensorflow)](https://github.com/tensorflow/tensorflow/releases/tag/v1.15.0) +[![Python 3.6](https://img.shields.io/badge/Python-3.6-3776AB)](https://www.python.org/downloads/release/python-360/) + +Creating accurate machine learning models capable of localizing and identifying +multiple objects in a single image remains a core challenge in computer vision. +The TensorFlow Object Detection API is an open source framework built on top of +TensorFlow that makes it easy to construct, train and deploy object detection +models. At Google we’ve certainly found this codebase to be useful for our +computer vision needs, and we hope that you will as well.

+

+Contributions to the codebase are welcome and we would love to hear back from +you if you find this API useful. Finally if you use the TensorFlow Object +Detection API for a research publication, please consider citing: + +``` +"Speed/accuracy trade-offs for modern convolutional object detectors." +Huang J, Rathod V, Sun C, Zhu M, Korattikara A, Fathi A, Fischer I, Wojna Z, +Song Y, Guadarrama S, Murphy K, CVPR 2017 +``` + +\[[link](https://arxiv.org/abs/1611.10012)\]\[[bibtex](https://scholar.googleusercontent.com/scholar.bib?q=info:l291WsrB-hQJ:scholar.google.com/&output=citation&scisig=AAGBfm0AAAAAWUIIlnPZ_L9jxvPwcC49kDlELtaeIyU-&scisf=4&ct=citation&cd=-1&hl=en&scfhb=1)\] + +

+ +

+ +## Support for TensorFlow 2 and 1 +The TensorFlow Object Detection API supports both TensorFlow 2 (TF2) and +TensorFlow 1 (TF1). A majority of the modules in the library are both TF1 and +TF2 compatible. In cases where they are not, we provide two versions. + +Although we will continue to maintain the TF1 models and provide support, we +encourage users to try the Object Detection API with TF2 for the following +reasons: + +* We provide new architectures supported in TF2 only and we will continue to + develop in TF2 going forward. + +* The popular models we ported from TF1 to TF2 achieve the same performance. + +* A single training and evaluation binary now supports both GPU and TPU + distribution strategies making it possible to train models with synchronous + SGD by default. + +* Eager execution with new binaries makes debugging easy! + +Finally, if are an existing user of the Object Detection API we have retained +the same config language you are familiar with and ensured that the +TF2 training/eval binary takes the same arguments as our TF1 binaries. + +Note: The models we provide in [TF2 Zoo](g3doc/tf2_detection_zoo.md) and +[TF1 Zoo](g3doc/tf1_detection_zoo.md) are specific to the TensorFlow major +version and are not interoperable. + +Please select one of the links below for TensorFlow version-specific +documentation of the Object Detection API: + + +### Tensorflow 2.x + * + Object Detection API TensorFlow 2
+ * + TensorFlow 2 Model Zoo
+ +### Tensorflow 1.x + * + Object Detection API TensorFlow 1
+ * + TensorFlow 1 Model Zoo
+ + +## Whats New + +### Mobile Inference for TF2 models + +TF2 OD API models can now be converted to TensorFlow Lite! Only SSD models +currently supported. See documentation. + +**Thanks to contributors**: Sachin Joglekar + +### TensorFlow 2 Support + +We are happy to announce that the TF OD API officially supports TF2! Our release +includes: + +* New binaries for train/eval/export that are designed to run in eager mode. +* A suite of TF2 compatible (Keras-based) models; this includes migrations of + our most popular TF1.x models (e.g., SSD with MobileNet, RetinaNet, + Faster R-CNN, Mask R-CNN), as well as a few new architectures for which we + will only maintain TF2 implementations: + + 1. CenterNet - a simple and effective anchor-free architecture based on + the recent [Objects as Points](https://arxiv.org/abs/1904.07850) paper by + Zhou et al. + 2. [EfficientDet](https://arxiv.org/abs/1911.09070) - a recent family of + SOTA models discovered with the help of Neural Architecture Search. + +* COCO pre-trained weights for all of the models provided as TF2 style + object-based checkpoints. +* Access to [Distribution Strategies](https://www.tensorflow.org/guide/distributed_training) + for distributed training --- our model are designed to be trainable using sync + multi-GPU and TPU platforms. +* Colabs demo’ing eager mode training and inference. + +See our release blogpost [here](https://blog.tensorflow.org/2020/07/tensorflow-2-meets-object-detection-api.html). +If you are an existing user of the TF OD API using TF 1.x, don’t worry, we’ve +got you covered. + +**Thanks to contributors**: Akhil Chinnakotla, Allen Lavoie, Anirudh Vegesana, +Anjali Sridhar, Austin Myers, Dan Kondratyuk, David Ross, Derek Chow, Jaeyoun +Kim, Jing Li, Jonathan Huang, Jordi Pont-Tuset, Karmel Allison, Kathy Ruan, +Kaushik Shivakumar, Lu He, Mingxing Tan, Pengchong Jin, Ronny Votel, Sara Beery, +Sergi Caelles Prat, Shan Yang, Sudheendra Vijayanarasimhan, Tina Tian, Tomer +Kaftan, Vighnesh Birodkar, Vishnu Banna, Vivek Rathod, Yanhui Liang, Yiming Shi, +Yixin Shi, Yu-hui Chen, Zhichao Lu. + +### MobileDet GPU + +We have released SSDLite with MobileDet GPU backbone, which achieves 17% mAP +higher than the MobileNetV2 SSDLite (27.5 mAP vs 23.5 mAP) on a NVIDIA Jetson +Xavier at comparable latency (3.2ms vs 3.3ms). + +Along with the model definition, we are also releasing model checkpoints trained +on the COCO dataset. + +Thanks to contributors: Yongzhe Wang, Bo Chen, Hanxiao Liu, Le An +(NVIDIA), Yu-Te Cheng (NVIDIA), Oliver Knieps (NVIDIA), and Josh Park (NVIDIA). + +### Context R-CNN + +We have released [Context R-CNN](https://arxiv.org/abs/1912.03538), a model that +uses attention to incorporate contextual information images (e.g. from +temporally nearby frames taken by a static camera) in order to improve accuracy. +Importantly, these contextual images need not be labeled. + +* When applied to a challenging wildlife detection dataset + ([Snapshot Serengeti](http://lila.science/datasets/snapshot-serengeti)), + Context R-CNN with context from up to a month of images outperforms a + single-frame baseline by 17.9% mAP, and outperforms S3D (a 3d convolution + based baseline) by 11.2% mAP. +* Context R-CNN leverages temporal context from the unlabeled frames of a + novel camera deployment to improve performance at that camera, boosting + model generalizeability. + +Read about Context R-CNN on the Google AI blog +[here](https://ai.googleblog.com/2020/06/leveraging-temporal-context-for-object.html). + +We have provided code for generating data with associated context +[here](g3doc/context_rcnn.md), and a sample config for a Context R-CNN model +[here](samples/configs/context_rcnn_resnet101_snapshot_serengeti_sync.config). + +Snapshot Serengeti-trained Faster R-CNN and Context R-CNN models can be found in +the +[model zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf1_detection_zoo.md#snapshot-serengeti-camera-trap-trained-models). + +A colab demonstrating Context R-CNN is provided +[here](colab_tutorials/context_rcnn_tutorial.ipynb). + +Thanks to contributors: Sara Beery, Jonathan Huang, Guanhang Wu, Vivek +Rathod, Ronny Votel, Zhichao Lu, David Ross, Pietro Perona, Tanya Birch, and the +Wildlife Insights AI Team. + +## Release Notes +See [notes](g3doc/release_notes.md) for all past releases. + +## Getting Help + +To get help with issues you may encounter using the TensorFlow Object Detection +API, create a new question on [StackOverflow](https://stackoverflow.com/) with +the tags "tensorflow" and "object-detection". + +Please report bugs (actually broken code, not usage questions) to the +tensorflow/models GitHub +[issue tracker](https://github.com/tensorflow/models/issues), prefixing the +issue name with "object_detection". + +Please check the [FAQ](g3doc/faq.md) for frequently asked questions before +reporting an issue. + +## Maintainers + +* Jonathan Huang ([@GitHub jch1](https://github.com/jch1)) +* Vivek Rathod ([@GitHub tombstone](https://github.com/tombstone)) +* Vighnesh Birodkar ([@GitHub vighneshbirodkar](https://github.com/vighneshbirodkar)) +* Austin Myers ([@GitHub austin-myers](https://github.com/austin-myers)) +* Zhichao Lu ([@GitHub pkulzc](https://github.com/pkulzc)) +* Ronny Votel ([@GitHub ronnyvotel](https://github.com/ronnyvotel)) +* Yu-hui Chen ([@GitHub yuhuichen1015](https://github.com/yuhuichen1015)) +* Derek Chow ([@GitHub derekjchow](https://github.com/derekjchow)) diff --git a/workspace/virtuallab/object_detection/__init__.py b/workspace/virtuallab/object_detection/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/virtuallab/object_detection/__init__.pyc b/workspace/virtuallab/object_detection/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae3d313c3a6d8e36e9edfa97c532322e0097b782 GIT binary patch literal 132 zcmZSn%*(a0Z&rLV0~9a&ryk0?N2?tPz4NzfePO2Tq+!7#W001j| B8}9%B literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/__pycache__/__init__.cpython-36.pyc b/workspace/virtuallab/object_detection/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98f48923319d4b5f5278347ba10c7dc7a01f1fe2 GIT binary patch literal 114 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 ZY-zYuk~=F=Y^433)QR%B(t*;c{|i{ZYwZ94 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/__pycache__/model_lib.cpython-36.pyc b/workspace/virtuallab/object_detection/__pycache__/model_lib.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..568e1077ed1aaccf9067e3d72aa7ff7ef69ce83c GIT binary patch literal 29863 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/__pycache__/flexible_grid_anchor_generator.cpython-36.pyc b/workspace/virtuallab/object_detection/anchor_generators/__pycache__/flexible_grid_anchor_generator.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4efc7d9afe638bc3546878dabf52800fa3c34000 GIT binary patch literal 6097 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 AQvd(} literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/__pycache__/grid_anchor_generator.cpython-36.pyc b/workspace/virtuallab/object_detection/anchor_generators/__pycache__/grid_anchor_generator.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4601f2bd61a048f8296fb5df18fcaea7ef8a3452 GIT binary patch literal 7754 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 ODnc`SGb{b#?f(K(kMT$V literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/__pycache__/multiple_grid_anchor_generator.cpython-36.pyc b/workspace/virtuallab/object_detection/anchor_generators/__pycache__/multiple_grid_anchor_generator.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23570daad8e4df46484c7ea0ce280b768f8037bd GIT binary patch literal 14376 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%0Agf!K{TEu1|u*v^v4+0HmVYM0fH^yok1Mj z8039|SOJVZ7Kb`kA7IhsQ;l*oux$f9Nt`<0R4U>5>uPbBX{seVl7iVb{|1t@2?qId7DrG z$;sOUo5kSOV(=;kfwLZ*Mr$Q8C6a_H9*V%QVyS2`?+5l?mp{Zz#@J9H! zq&5TRq;d%P`r-BbKz*26d;nY8J%ZtF9d4O%qct{D)V4OE+zcQVZBPU^iDC{7jG1Xv z24QsA&&hU^8D9Gut^2S~$fe+%j3AC+jIe})FroPJG^oU>dZL=6gk*F4YM~o;jh+5K za;p#XRG!i9otxn-=e!jkz6}tfOsW~iq5g7Wgaz?ZM3|}9d{ikVrIn<;em*m4bI<+q zT|ZC&wE2qP`r$ zp^#k7E*cwY3@BHSk|aSAovjAw(C=2eA{p-TWi%y-iu-+d6u|IvXFJsKUv~|U0&y@xRHF?D z%2HrKzjGHSSnLf&?;&a^;M4YURyhvF8ZuIKyRkdZcVQn=Jx~nJxq5Ue~>L2kI?mqL7#+=4Fp{% zzOjk-Fs5#yv0ifRhJB&ZK)ZsA{;R;oEq$%x-m+cbWb?Ez3d!+|Ct)^ zlsn>c-~G0uO;k7S2H|uKZ@sMPd8oc7q42z{<#~@LewfeHJ&(sd&y%l!S9yh+?^3fx zjXv3|A@HwZI0;NqrO~k6N^`AIX*BhCyCr{&(QMUN0;O|I${5q7ue^vzBxD6djNzPn^YrJ$S+Ov822;D-FoL*w35)d^T&Lxb I7Fw160P#HO`v3p{ literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.py b/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.py new file mode 100644 index 0000000..0f340cc --- /dev/null +++ b/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.py @@ -0,0 +1,134 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Generates grid anchors on the fly corresponding to multiple CNN layers.""" + +import tensorflow.compat.v1 as tf + +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.core import anchor_generator +from object_detection.core import box_list_ops + + +class FlexibleGridAnchorGenerator(anchor_generator.AnchorGenerator): + """Generate a grid of anchors for multiple CNN layers of different scale.""" + + def __init__(self, base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=True): + """Constructs a FlexibleGridAnchorGenerator. + + This generator is more flexible than the multiple_grid_anchor_generator + and multiscale_grid_anchor_generator, and can generate any of the anchors + that they can generate, plus additional anchor configurations. In + particular, it allows the explicit specification of scale and aspect ratios + at each layer without making any assumptions between the relationship + between scales and aspect ratios between layers. + + Args: + base_sizes: list of tuples of anchor base sizes. For example, setting + base_sizes=[(1, 2, 3), (4, 5)] means that we want 3 anchors at each + grid point on the first layer with the base sizes of 1, 2, and 3, and 2 + anchors at each grid point on the second layer with the base sizes of + 4 and 5. + aspect_ratios: list or tuple of aspect ratios. For example, setting + aspect_ratios=[(1.0, 2.0, 0.5), (1.0, 2.0)] means that we want 3 anchors + at each grid point on the first layer with aspect ratios of 1.0, 2.0, + and 0.5, and 2 anchors at each grid point on the sercond layer with the + base sizes of 1.0 and 2.0. + anchor_strides: list of pairs of strides in pixels (in y and x directions + respectively). For example, setting anchor_strides=[(25, 25), (50, 50)] + means that we want the anchors corresponding to the first layer to be + strided by 25 pixels and those in the second layer to be strided by 50 + pixels in both y and x directions. + anchor_offsets: list of pairs of offsets in pixels (in y and x directions + respectively). The offset specifies where we want the center of the + (0, 0)-th anchor to lie for each layer. For example, setting + anchor_offsets=[(10, 10), (20, 20)]) means that we want the + (0, 0)-th anchor of the first layer to lie at (10, 10) in pixel space + and likewise that we want the (0, 0)-th anchor of the second layer to + lie at (25, 25) in pixel space. + normalize_coordinates: whether to produce anchors in normalized + coordinates. (defaults to True). + """ + self._base_sizes = base_sizes + self._aspect_ratios = aspect_ratios + self._anchor_strides = anchor_strides + self._anchor_offsets = anchor_offsets + self._normalize_coordinates = normalize_coordinates + + def name_scope(self): + return 'FlexibleGridAnchorGenerator' + + def num_anchors_per_location(self): + """Returns the number of anchors per spatial location. + + Returns: + a list of integers, one for each expected feature map to be passed to + the Generate function. + """ + return [len(size) for size in self._base_sizes] + + def _generate(self, feature_map_shape_list, im_height=1, im_width=1): + """Generates a collection of bounding boxes to be used as anchors. + + Currently we require the input image shape to be statically defined. That + is, im_height and im_width should be integers rather than tensors. + + Args: + feature_map_shape_list: list of pairs of convnet layer resolutions in the + format [(height_0, width_0), (height_1, width_1), ...]. For example, + setting feature_map_shape_list=[(8, 8), (7, 7)] asks for anchors that + correspond to an 8x8 layer followed by a 7x7 layer. + im_height: the height of the image to generate the grid for. If both + im_height and im_width are 1, anchors can only be generated in + absolute coordinates. + im_width: the width of the image to generate the grid for. If both + im_height and im_width are 1, anchors can only be generated in + absolute coordinates. + + Returns: + boxes_list: a list of BoxLists each holding anchor boxes corresponding to + the input feature map shapes. + Raises: + ValueError: if im_height and im_width are 1, but normalized coordinates + were requested. + """ + anchor_grid_list = [] + for (feat_shape, base_sizes, aspect_ratios, anchor_stride, anchor_offset + ) in zip(feature_map_shape_list, self._base_sizes, self._aspect_ratios, + self._anchor_strides, self._anchor_offsets): + anchor_grid = grid_anchor_generator.tile_anchors( + feat_shape[0], + feat_shape[1], + tf.cast(tf.convert_to_tensor(base_sizes), dtype=tf.float32), + tf.cast(tf.convert_to_tensor(aspect_ratios), dtype=tf.float32), + tf.constant([1.0, 1.0]), + tf.cast(tf.convert_to_tensor(anchor_stride), dtype=tf.float32), + tf.cast(tf.convert_to_tensor(anchor_offset), dtype=tf.float32)) + num_anchors = anchor_grid.num_boxes_static() + if num_anchors is None: + num_anchors = anchor_grid.num_boxes() + anchor_indices = tf.zeros([num_anchors]) + anchor_grid.add_field('feature_map_index', anchor_indices) + if self._normalize_coordinates: + if im_height == 1 or im_width == 1: + raise ValueError( + 'Normalized coordinates were requested upon construction of the ' + 'FlexibleGridAnchorGenerator, but a subsequent call to ' + 'generate did not supply dimension information.') + anchor_grid = box_list_ops.to_normalized_coordinates( + anchor_grid, im_height, im_width, check_range=False) + anchor_grid_list.append(anchor_grid) + + return anchor_grid_list diff --git a/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.pyc b/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4462f094cf23451e15a5eb4c3be4b1c05fc7b3e GIT binary patch literal 6569 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator_test.py b/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator_test.py new file mode 100644 index 0000000..bab34b7 --- /dev/null +++ b/workspace/virtuallab/object_detection/anchor_generators/flexible_grid_anchor_generator_test.py @@ -0,0 +1,292 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for anchor_generators.flexible_grid_anchor_generator_test.py.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.anchor_generators import flexible_grid_anchor_generator as fg +from object_detection.utils import test_case + + +class FlexibleGridAnchorGeneratorTest(test_case.TestCase): + + def test_construct_single_anchor(self): + def graph_fn(): + anchor_strides = [(32, 32),] + anchor_offsets = [(16, 16),] + base_sizes = [(128.0,)] + aspect_ratios = [(1.0,)] + im_height = 64 + im_width = 64 + feature_map_shape_list = [(2, 2)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + anchor_corners_out = self.execute(graph_fn, []) + exp_anchor_corners = [[-48, -48, 80, 80], + [-48, -16, 80, 112], + [-16, -48, 112, 80], + [-16, -16, 112, 112]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_unit_dimensions(self): + def graph_fn(): + anchor_strides = [(32, 32),] + anchor_offsets = [(16, 16),] + base_sizes = [(32.0,)] + aspect_ratios = [(1.0,)] + im_height = 1 + im_width = 1 + feature_map_shape_list = [(2, 2)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + # Positive offsets are produced. + exp_anchor_corners = [[0, 0, 32, 32], + [0, 32, 32, 64], + [32, 0, 64, 32], + [32, 32, 64, 64]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_normalized_anchors_fails_with_unit_dimensions(self): + anchor_generator = fg.FlexibleGridAnchorGenerator( + [(32.0,)], [(1.0,)], [(32, 32),], [(16, 16),], + normalize_coordinates=True) + with self.assertRaisesRegexp(ValueError, 'Normalized coordinates'): + anchor_generator.generate( + feature_map_shape_list=[(2, 2)], im_height=1, im_width=1) + + def test_construct_single_anchor_in_normalized_coordinates(self): + def graph_fn(): + anchor_strides = [(32, 32),] + anchor_offsets = [(16, 16),] + base_sizes = [(128.0,)] + aspect_ratios = [(1.0,)] + im_height = 64 + im_width = 128 + feature_map_shape_list = [(2, 2)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=True) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + exp_anchor_corners = [[-48./64, -48./128, 80./64, 80./128], + [-48./64, -16./128, 80./64, 112./128], + [-16./64, -48./128, 112./64, 80./128], + [-16./64, -16./128, 112./64, 112./128]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_num_anchors_per_location(self): + anchor_strides = [(32, 32), (64, 64)] + anchor_offsets = [(16, 16), (32, 32)] + base_sizes = [(32.0, 64.0, 96.0, 32.0, 64.0, 96.0), + (64.0, 128.0, 172.0, 64.0, 128.0, 172.0)] + aspect_ratios = [(1.0, 1.0, 1.0, 2.0, 2.0, 2.0), + (1.0, 1.0, 1.0, 2.0, 2.0, 2.0)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + self.assertEqual(anchor_generator.num_anchors_per_location(), [6, 6]) + + def test_construct_single_anchor_dynamic_size(self): + def graph_fn(): + anchor_strides = [(32, 32),] + anchor_offsets = [(0, 0),] + base_sizes = [(128.0,)] + aspect_ratios = [(1.0,)] + im_height = tf.constant(64) + im_width = tf.constant(64) + feature_map_shape_list = [(2, 2)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + # Zero offsets are used. + exp_anchor_corners = [[-64, -64, 64, 64], + [-64, -32, 64, 96], + [-32, -64, 96, 64], + [-32, -32, 96, 96]] + anchor_corners_out = self.execute_cpu(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_with_odd_input_dimension(self): + + def graph_fn(): + anchor_strides = [(32, 32),] + anchor_offsets = [(0, 0),] + base_sizes = [(128.0,)] + aspect_ratios = [(1.0,)] + im_height = 65 + im_width = 65 + feature_map_shape_list = [(3, 3)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return (anchor_corners,) + anchor_corners_out = self.execute(graph_fn, []) + exp_anchor_corners = [[-64, -64, 64, 64], + [-64, -32, 64, 96], + [-64, 0, 64, 128], + [-32, -64, 96, 64], + [-32, -32, 96, 96], + [-32, 0, 96, 128], + [0, -64, 128, 64], + [0, -32, 128, 96], + [0, 0, 128, 128]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_on_two_feature_maps(self): + + def graph_fn(): + anchor_strides = [(32, 32), (64, 64)] + anchor_offsets = [(16, 16), (32, 32)] + base_sizes = [(128.0,), (256.0,)] + aspect_ratios = [(1.0,), (1.0,)] + im_height = 64 + im_width = 64 + feature_map_shape_list = [(2, 2), (1, 1)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + + anchor_corners_out = np.concatenate(self.execute(graph_fn, []), axis=0) + exp_anchor_corners = [[-48, -48, 80, 80], + [-48, -16, 80, 112], + [-16, -48, 112, 80], + [-16, -16, 112, 112], + [-96, -96, 160, 160]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_with_two_scales_per_octave(self): + + def graph_fn(): + anchor_strides = [(64, 64),] + anchor_offsets = [(32, 32),] + base_sizes = [(256.0, 362.03867)] + aspect_ratios = [(1.0, 1.0)] + im_height = 64 + im_width = 64 + feature_map_shape_list = [(1, 1)] + + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + # There are 4 set of anchors in this configuration. The order is: + # [[2**0.0 intermediate scale + 1.0 aspect], + # [2**0.5 intermediate scale + 1.0 aspect]] + exp_anchor_corners = [[-96., -96., 160., 160.], + [-149.0193, -149.0193, 213.0193, 213.0193]] + + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_with_two_scales_per_octave_and_aspect(self): + def graph_fn(): + anchor_strides = [(64, 64),] + anchor_offsets = [(32, 32),] + base_sizes = [(256.0, 362.03867, 256.0, 362.03867)] + aspect_ratios = [(1.0, 1.0, 2.0, 2.0)] + im_height = 64 + im_width = 64 + feature_map_shape_list = [(1, 1)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + # There are 4 set of anchors in this configuration. The order is: + # [[2**0.0 intermediate scale + 1.0 aspect], + # [2**0.5 intermediate scale + 1.0 aspect], + # [2**0.0 intermediate scale + 2.0 aspect], + # [2**0.5 intermediate scale + 2.0 aspect]] + + exp_anchor_corners = [[-96., -96., 160., 160.], + [-149.0193, -149.0193, 213.0193, 213.0193], + [-58.50967, -149.0193, 122.50967, 213.0193], + [-96., -224., 160., 288.]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchors_on_feature_maps_with_dynamic_shape(self): + + def graph_fn(feature_map1_height, feature_map1_width, feature_map2_height, + feature_map2_width): + anchor_strides = [(32, 32), (64, 64)] + anchor_offsets = [(16, 16), (32, 32)] + base_sizes = [(128.0,), (256.0,)] + aspect_ratios = [(1.0,), (1.0,)] + im_height = 64 + im_width = 64 + feature_map_shape_list = [(feature_map1_height, feature_map1_width), + (feature_map2_height, feature_map2_width)] + anchor_generator = fg.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, anchor_strides, anchor_offsets, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + + anchor_corners_out = np.concatenate( + self.execute_cpu(graph_fn, [ + np.array(2, dtype=np.int32), + np.array(2, dtype=np.int32), + np.array(1, dtype=np.int32), + np.array(1, dtype=np.int32) + ]), + axis=0) + exp_anchor_corners = [[-48, -48, 80, 80], + [-48, -16, 80, 112], + [-16, -48, 112, 80], + [-16, -16, 112, 112], + [-96, -96, 160, 160]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.py b/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.py new file mode 100644 index 0000000..a31bc87 --- /dev/null +++ b/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.py @@ -0,0 +1,213 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Generates grid anchors on the fly as used in Faster RCNN. + +Generates grid anchors on the fly as described in: +"Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks" +Shaoqing Ren, Kaiming He, Ross Girshick, and Jian Sun. +""" + +import tensorflow.compat.v1 as tf + +from object_detection.core import anchor_generator +from object_detection.core import box_list +from object_detection.utils import ops + + +class GridAnchorGenerator(anchor_generator.AnchorGenerator): + """Generates a grid of anchors at given scales and aspect ratios.""" + + def __init__(self, + scales=(0.5, 1.0, 2.0), + aspect_ratios=(0.5, 1.0, 2.0), + base_anchor_size=None, + anchor_stride=None, + anchor_offset=None): + """Constructs a GridAnchorGenerator. + + Args: + scales: a list of (float) scales, default=(0.5, 1.0, 2.0) + aspect_ratios: a list of (float) aspect ratios, default=(0.5, 1.0, 2.0) + base_anchor_size: base anchor size as height, width ( + (length-2 float32 list or tensor, default=[256, 256]) + anchor_stride: difference in centers between base anchors for adjacent + grid positions (length-2 float32 list or tensor, + default=[16, 16]) + anchor_offset: center of the anchor with scale and aspect ratio 1 for the + upper left element of the grid, this should be zero for + feature networks with only VALID padding and even receptive + field size, but may need additional calculation if other + padding is used (length-2 float32 list or tensor, + default=[0, 0]) + """ + # Handle argument defaults + if base_anchor_size is None: + base_anchor_size = [256, 256] + if anchor_stride is None: + anchor_stride = [16, 16] + if anchor_offset is None: + anchor_offset = [0, 0] + + self._scales = scales + self._aspect_ratios = aspect_ratios + self._base_anchor_size = base_anchor_size + self._anchor_stride = anchor_stride + self._anchor_offset = anchor_offset + + def name_scope(self): + return 'GridAnchorGenerator' + + def num_anchors_per_location(self): + """Returns the number of anchors per spatial location. + + Returns: + a list of integers, one for each expected feature map to be passed to + the `generate` function. + """ + return [len(self._scales) * len(self._aspect_ratios)] + + def _generate(self, feature_map_shape_list): + """Generates a collection of bounding boxes to be used as anchors. + + Args: + feature_map_shape_list: list of pairs of convnet layer resolutions in the + format [(height_0, width_0)]. For example, setting + feature_map_shape_list=[(8, 8)] asks for anchors that correspond + to an 8x8 layer. For this anchor generator, only lists of length 1 are + allowed. + + Returns: + boxes_list: a list of BoxLists each holding anchor boxes corresponding to + the input feature map shapes. + + Raises: + ValueError: if feature_map_shape_list, box_specs_list do not have the same + length. + ValueError: if feature_map_shape_list does not consist of pairs of + integers + """ + if not (isinstance(feature_map_shape_list, list) + and len(feature_map_shape_list) == 1): + raise ValueError('feature_map_shape_list must be a list of length 1.') + if not all([isinstance(list_item, tuple) and len(list_item) == 2 + for list_item in feature_map_shape_list]): + raise ValueError('feature_map_shape_list must be a list of pairs.') + + # Create constants in init_scope so they can be created in tf.functions + # and accessed from outside of the function. + with tf.init_scope(): + self._base_anchor_size = tf.cast(tf.convert_to_tensor( + self._base_anchor_size), dtype=tf.float32) + self._anchor_stride = tf.cast(tf.convert_to_tensor( + self._anchor_stride), dtype=tf.float32) + self._anchor_offset = tf.cast(tf.convert_to_tensor( + self._anchor_offset), dtype=tf.float32) + + grid_height, grid_width = feature_map_shape_list[0] + scales_grid, aspect_ratios_grid = ops.meshgrid(self._scales, + self._aspect_ratios) + scales_grid = tf.reshape(scales_grid, [-1]) + aspect_ratios_grid = tf.reshape(aspect_ratios_grid, [-1]) + anchors = tile_anchors(grid_height, + grid_width, + scales_grid, + aspect_ratios_grid, + self._base_anchor_size, + self._anchor_stride, + self._anchor_offset) + + num_anchors = anchors.num_boxes_static() + if num_anchors is None: + num_anchors = anchors.num_boxes() + anchor_indices = tf.zeros([num_anchors]) + anchors.add_field('feature_map_index', anchor_indices) + return [anchors] + + +def tile_anchors(grid_height, + grid_width, + scales, + aspect_ratios, + base_anchor_size, + anchor_stride, + anchor_offset): + """Create a tiled set of anchors strided along a grid in image space. + + This op creates a set of anchor boxes by placing a "basis" collection of + boxes with user-specified scales and aspect ratios centered at evenly + distributed points along a grid. The basis collection is specified via the + scale and aspect_ratios arguments. For example, setting scales=[.1, .2, .2] + and aspect ratios = [2,2,1/2] means that we create three boxes: one with scale + .1, aspect ratio 2, one with scale .2, aspect ratio 2, and one with scale .2 + and aspect ratio 1/2. Each box is multiplied by "base_anchor_size" before + placing it over its respective center. + + Grid points are specified via grid_height, grid_width parameters as well as + the anchor_stride and anchor_offset parameters. + + Args: + grid_height: size of the grid in the y direction (int or int scalar tensor) + grid_width: size of the grid in the x direction (int or int scalar tensor) + scales: a 1-d (float) tensor representing the scale of each box in the + basis set. + aspect_ratios: a 1-d (float) tensor representing the aspect ratio of each + box in the basis set. The length of the scales and aspect_ratios tensors + must be equal. + base_anchor_size: base anchor size as [height, width] + (float tensor of shape [2]) + anchor_stride: difference in centers between base anchors for adjacent grid + positions (float tensor of shape [2]) + anchor_offset: center of the anchor with scale and aspect ratio 1 for the + upper left element of the grid, this should be zero for + feature networks with only VALID padding and even receptive + field size, but may need some additional calculation if other + padding is used (float tensor of shape [2]) + Returns: + a BoxList holding a collection of N anchor boxes + """ + ratio_sqrts = tf.sqrt(aspect_ratios) + heights = scales / ratio_sqrts * base_anchor_size[0] + widths = scales * ratio_sqrts * base_anchor_size[1] + + # Get a grid of box centers + y_centers = tf.cast(tf.range(grid_height), dtype=tf.float32) + y_centers = y_centers * anchor_stride[0] + anchor_offset[0] + x_centers = tf.cast(tf.range(grid_width), dtype=tf.float32) + x_centers = x_centers * anchor_stride[1] + anchor_offset[1] + x_centers, y_centers = ops.meshgrid(x_centers, y_centers) + + widths_grid, x_centers_grid = ops.meshgrid(widths, x_centers) + heights_grid, y_centers_grid = ops.meshgrid(heights, y_centers) + bbox_centers = tf.stack([y_centers_grid, x_centers_grid], axis=3) + bbox_sizes = tf.stack([heights_grid, widths_grid], axis=3) + bbox_centers = tf.reshape(bbox_centers, [-1, 2]) + bbox_sizes = tf.reshape(bbox_sizes, [-1, 2]) + bbox_corners = _center_size_bbox_to_corners_bbox(bbox_centers, bbox_sizes) + return box_list.BoxList(bbox_corners) + + +def _center_size_bbox_to_corners_bbox(centers, sizes): + """Converts bbox center-size representation to corners representation. + + Args: + centers: a tensor with shape [N, 2] representing bounding box centers + sizes: a tensor with shape [N, 2] representing bounding boxes + + Returns: + corners: tensor with shape [N, 4] representing bounding boxes in corners + representation + """ + return tf.concat([centers - .5 * sizes, centers + .5 * sizes], 1) diff --git a/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.pyc b/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cc1d568630e15d3de2418f313a0bf920079a4f9 GIT binary patch literal 8626 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 TmK>QYuOQ{dU*&Rd;qCtb=O#jS literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator_test.py b/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator_test.py new file mode 100644 index 0000000..292076e --- /dev/null +++ b/workspace/virtuallab/object_detection/anchor_generators/grid_anchor_generator_test.py @@ -0,0 +1,104 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.grid_anchor_generator.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.utils import test_case + + +class GridAnchorGeneratorTest(test_case.TestCase): + + def test_construct_single_anchor(self): + """Builds a 1x1 anchor grid to test the size of the output boxes.""" + def graph_fn(): + scales = [0.5, 1.0, 2.0] + aspect_ratios = [0.25, 1.0, 4.0] + anchor_offset = [7, -3] + anchor_generator = grid_anchor_generator.GridAnchorGenerator( + scales, aspect_ratios, anchor_offset=anchor_offset) + anchors_list = anchor_generator.generate(feature_map_shape_list=[(1, 1)]) + anchor_corners = anchors_list[0].get() + return (anchor_corners,) + exp_anchor_corners = [[-121, -35, 135, 29], [-249, -67, 263, 61], + [-505, -131, 519, 125], [-57, -67, 71, 61], + [-121, -131, 135, 125], [-249, -259, 263, 253], + [-25, -131, 39, 125], [-57, -259, 71, 253], + [-121, -515, 135, 509]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_anchor_grid(self): + def graph_fn(): + base_anchor_size = [10, 10] + anchor_stride = [19, 19] + anchor_offset = [0, 0] + scales = [0.5, 1.0, 2.0] + aspect_ratios = [1.0] + + anchor_generator = grid_anchor_generator.GridAnchorGenerator( + scales, + aspect_ratios, + base_anchor_size=base_anchor_size, + anchor_stride=anchor_stride, + anchor_offset=anchor_offset) + + anchors_list = anchor_generator.generate(feature_map_shape_list=[(2, 2)]) + anchor_corners = anchors_list[0].get() + return (anchor_corners,) + exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.], + [-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5], + [-5., 14., 5, 24], [-10., 9., 10, 29], + [16.5, -2.5, 21.5, 2.5], [14., -5., 24, 5], + [9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5], + [14., 14., 24, 24], [9., 9., 29, 29]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_anchor_grid_with_dynamic_feature_map_shapes(self): + def graph_fn(feature_map_height, feature_map_width): + base_anchor_size = [10, 10] + anchor_stride = [19, 19] + anchor_offset = [0, 0] + scales = [0.5, 1.0, 2.0] + aspect_ratios = [1.0] + anchor_generator = grid_anchor_generator.GridAnchorGenerator( + scales, + aspect_ratios, + base_anchor_size=base_anchor_size, + anchor_stride=anchor_stride, + anchor_offset=anchor_offset) + + anchors_list = anchor_generator.generate( + feature_map_shape_list=[(feature_map_height, feature_map_width)]) + anchor_corners = anchors_list[0].get() + return (anchor_corners,) + + exp_anchor_corners = [[-2.5, -2.5, 2.5, 2.5], [-5., -5., 5., 5.], + [-10., -10., 10., 10.], [-2.5, 16.5, 2.5, 21.5], + [-5., 14., 5, 24], [-10., 9., 10, 29], + [16.5, -2.5, 21.5, 2.5], [14., -5., 24, 5], + [9., -10., 29, 10], [16.5, 16.5, 21.5, 21.5], + [14., 14., 24, 24], [9., 9., 29, 29]] + anchor_corners_out = self.execute_cpu(graph_fn, + [np.array(2, dtype=np.int32), + np.array(2, dtype=np.int32)]) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.py b/workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.py new file mode 100644 index 0000000..5da24d4 --- /dev/null +++ b/workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.py @@ -0,0 +1,342 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Generates grid anchors on the fly corresponding to multiple CNN layers. + +Generates grid anchors on the fly corresponding to multiple CNN layers as +described in: +"SSD: Single Shot MultiBox Detector" +Wei Liu, Dragomir Anguelov, Dumitru Erhan, Christian Szegedy, Scott Reed, +Cheng-Yang Fu, Alexander C. Berg +(see Section 2.2: Choosing scales and aspect ratios for default boxes) +""" + +import numpy as np + +import tensorflow.compat.v1 as tf + +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.core import anchor_generator +from object_detection.core import box_list_ops + + +class MultipleGridAnchorGenerator(anchor_generator.AnchorGenerator): + """Generate a grid of anchors for multiple CNN layers.""" + + def __init__(self, + box_specs_list, + base_anchor_size=None, + anchor_strides=None, + anchor_offsets=None, + clip_window=None): + """Constructs a MultipleGridAnchorGenerator. + + To construct anchors, at multiple grid resolutions, one must provide a + list of feature_map_shape_list (e.g., [(8, 8), (4, 4)]), and for each grid + size, a corresponding list of (scale, aspect ratio) box specifications. + + For example: + box_specs_list = [[(.1, 1.0), (.1, 2.0)], # for 8x8 grid + [(.2, 1.0), (.3, 1.0), (.2, 2.0)]] # for 4x4 grid + + To support the fully convolutional setting, we pass grid sizes in at + generation time, while scale and aspect ratios are fixed at construction + time. + + Args: + box_specs_list: list of list of (scale, aspect ratio) pairs with the + outside list having the same number of entries as feature_map_shape_list + (which is passed in at generation time). + base_anchor_size: base anchor size as [height, width] + (length-2 float numpy or Tensor, default=[1.0, 1.0]). + The height and width values are normalized to the + minimum dimension of the input height and width, so that + when the base anchor height equals the base anchor + width, the resulting anchor is square even if the input + image is not square. + anchor_strides: list of pairs of strides in pixels (in y and x directions + respectively). For example, setting anchor_strides=[(25, 25), (50, 50)] + means that we want the anchors corresponding to the first layer to be + strided by 25 pixels and those in the second layer to be strided by 50 + pixels in both y and x directions. If anchor_strides=None, they are set + to be the reciprocal of the corresponding feature map shapes. + anchor_offsets: list of pairs of offsets in pixels (in y and x directions + respectively). The offset specifies where we want the center of the + (0, 0)-th anchor to lie for each layer. For example, setting + anchor_offsets=[(10, 10), (20, 20)]) means that we want the + (0, 0)-th anchor of the first layer to lie at (10, 10) in pixel space + and likewise that we want the (0, 0)-th anchor of the second layer to + lie at (25, 25) in pixel space. If anchor_offsets=None, then they are + set to be half of the corresponding anchor stride. + clip_window: a tensor of shape [4] specifying a window to which all + anchors should be clipped. If clip_window is None, then no clipping + is performed. + + Raises: + ValueError: if box_specs_list is not a list of list of pairs + ValueError: if clip_window is not either None or a tensor of shape [4] + """ + if isinstance(box_specs_list, list) and all( + [isinstance(list_item, list) for list_item in box_specs_list]): + self._box_specs = box_specs_list + else: + raise ValueError('box_specs_list is expected to be a ' + 'list of lists of pairs') + if base_anchor_size is None: + base_anchor_size = [256, 256] + self._base_anchor_size = base_anchor_size + self._anchor_strides = anchor_strides + self._anchor_offsets = anchor_offsets + if clip_window is not None and clip_window.get_shape().as_list() != [4]: + raise ValueError('clip_window must either be None or a shape [4] tensor') + self._clip_window = clip_window + self._scales = [] + self._aspect_ratios = [] + for box_spec in self._box_specs: + if not all([isinstance(entry, tuple) and len(entry) == 2 + for entry in box_spec]): + raise ValueError('box_specs_list is expected to be a ' + 'list of lists of pairs') + scales, aspect_ratios = zip(*box_spec) + self._scales.append(scales) + self._aspect_ratios.append(aspect_ratios) + + for arg, arg_name in zip([self._anchor_strides, self._anchor_offsets], + ['anchor_strides', 'anchor_offsets']): + if arg and not (isinstance(arg, list) and + len(arg) == len(self._box_specs)): + raise ValueError('%s must be a list with the same length ' + 'as self._box_specs' % arg_name) + if arg and not all([ + isinstance(list_item, tuple) and len(list_item) == 2 + for list_item in arg + ]): + raise ValueError('%s must be a list of pairs.' % arg_name) + + def name_scope(self): + return 'MultipleGridAnchorGenerator' + + def num_anchors_per_location(self): + """Returns the number of anchors per spatial location. + + Returns: + a list of integers, one for each expected feature map to be passed to + the Generate function. + """ + return [len(box_specs) for box_specs in self._box_specs] + + def _generate(self, feature_map_shape_list, im_height=1, im_width=1): + """Generates a collection of bounding boxes to be used as anchors. + + The number of anchors generated for a single grid with shape MxM where we + place k boxes over each grid center is k*M^2 and thus the total number of + anchors is the sum over all grids. In our box_specs_list example + (see the constructor docstring), we would place two boxes over each grid + point on an 8x8 grid and three boxes over each grid point on a 4x4 grid and + thus end up with 2*8^2 + 3*4^2 = 176 anchors in total. The layout of the + output anchors follows the order of how the grid sizes and box_specs are + specified (with box_spec index varying the fastest, followed by width + index, then height index, then grid index). + + Args: + feature_map_shape_list: list of pairs of convnet layer resolutions in the + format [(height_0, width_0), (height_1, width_1), ...]. For example, + setting feature_map_shape_list=[(8, 8), (7, 7)] asks for anchors that + correspond to an 8x8 layer followed by a 7x7 layer. + im_height: the height of the image to generate the grid for. If both + im_height and im_width are 1, the generated anchors default to + absolute coordinates, otherwise normalized coordinates are produced. + im_width: the width of the image to generate the grid for. If both + im_height and im_width are 1, the generated anchors default to + absolute coordinates, otherwise normalized coordinates are produced. + + Returns: + boxes_list: a list of BoxLists each holding anchor boxes corresponding to + the input feature map shapes. + + Raises: + ValueError: if feature_map_shape_list, box_specs_list do not have the same + length. + ValueError: if feature_map_shape_list does not consist of pairs of + integers + """ + if not (isinstance(feature_map_shape_list, list) + and len(feature_map_shape_list) == len(self._box_specs)): + raise ValueError('feature_map_shape_list must be a list with the same ' + 'length as self._box_specs') + if not all([isinstance(list_item, tuple) and len(list_item) == 2 + for list_item in feature_map_shape_list]): + raise ValueError('feature_map_shape_list must be a list of pairs.') + + im_height = tf.cast(im_height, dtype=tf.float32) + im_width = tf.cast(im_width, dtype=tf.float32) + + if not self._anchor_strides: + anchor_strides = [(1.0 / tf.cast(pair[0], dtype=tf.float32), + 1.0 / tf.cast(pair[1], dtype=tf.float32)) + for pair in feature_map_shape_list] + else: + anchor_strides = [(tf.cast(stride[0], dtype=tf.float32) / im_height, + tf.cast(stride[1], dtype=tf.float32) / im_width) + for stride in self._anchor_strides] + if not self._anchor_offsets: + anchor_offsets = [(0.5 * stride[0], 0.5 * stride[1]) + for stride in anchor_strides] + else: + anchor_offsets = [(tf.cast(offset[0], dtype=tf.float32) / im_height, + tf.cast(offset[1], dtype=tf.float32) / im_width) + for offset in self._anchor_offsets] + + for arg, arg_name in zip([anchor_strides, anchor_offsets], + ['anchor_strides', 'anchor_offsets']): + if not (isinstance(arg, list) and len(arg) == len(self._box_specs)): + raise ValueError('%s must be a list with the same length ' + 'as self._box_specs' % arg_name) + if not all([isinstance(list_item, tuple) and len(list_item) == 2 + for list_item in arg]): + raise ValueError('%s must be a list of pairs.' % arg_name) + + anchor_grid_list = [] + min_im_shape = tf.minimum(im_height, im_width) + scale_height = min_im_shape / im_height + scale_width = min_im_shape / im_width + if not tf.is_tensor(self._base_anchor_size): + base_anchor_size = [ + scale_height * tf.constant(self._base_anchor_size[0], + dtype=tf.float32), + scale_width * tf.constant(self._base_anchor_size[1], + dtype=tf.float32) + ] + else: + base_anchor_size = [ + scale_height * self._base_anchor_size[0], + scale_width * self._base_anchor_size[1] + ] + for feature_map_index, (grid_size, scales, aspect_ratios, stride, + offset) in enumerate( + zip(feature_map_shape_list, self._scales, + self._aspect_ratios, anchor_strides, + anchor_offsets)): + tiled_anchors = grid_anchor_generator.tile_anchors( + grid_height=grid_size[0], + grid_width=grid_size[1], + scales=scales, + aspect_ratios=aspect_ratios, + base_anchor_size=base_anchor_size, + anchor_stride=stride, + anchor_offset=offset) + if self._clip_window is not None: + tiled_anchors = box_list_ops.clip_to_window( + tiled_anchors, self._clip_window, filter_nonoverlapping=False) + num_anchors_in_layer = tiled_anchors.num_boxes_static() + if num_anchors_in_layer is None: + num_anchors_in_layer = tiled_anchors.num_boxes() + anchor_indices = feature_map_index * tf.ones([num_anchors_in_layer]) + tiled_anchors.add_field('feature_map_index', anchor_indices) + anchor_grid_list.append(tiled_anchors) + + return anchor_grid_list + + +def create_ssd_anchors(num_layers=6, + min_scale=0.2, + max_scale=0.95, + scales=None, + aspect_ratios=(1.0, 2.0, 3.0, 1.0 / 2, 1.0 / 3), + interpolated_scale_aspect_ratio=1.0, + base_anchor_size=None, + anchor_strides=None, + anchor_offsets=None, + reduce_boxes_in_lowest_layer=True): + """Creates MultipleGridAnchorGenerator for SSD anchors. + + This function instantiates a MultipleGridAnchorGenerator that reproduces + ``default box`` construction proposed by Liu et al in the SSD paper. + See Section 2.2 for details. Grid sizes are assumed to be passed in + at generation time from finest resolution to coarsest resolution --- this is + used to (linearly) interpolate scales of anchor boxes corresponding to the + intermediate grid sizes. + + Anchors that are returned by calling the `generate` method on the returned + MultipleGridAnchorGenerator object are always in normalized coordinates + and clipped to the unit square: (i.e. all coordinates lie in [0, 1]x[0, 1]). + + Args: + num_layers: integer number of grid layers to create anchors for (actual + grid sizes passed in at generation time) + min_scale: scale of anchors corresponding to finest resolution (float) + max_scale: scale of anchors corresponding to coarsest resolution (float) + scales: As list of anchor scales to use. When not None and not empty, + min_scale and max_scale are not used. + aspect_ratios: list or tuple of (float) aspect ratios to place on each + grid point. + interpolated_scale_aspect_ratio: An additional anchor is added with this + aspect ratio and a scale interpolated between the scale for a layer + and the scale for the next layer (1.0 for the last layer). + This anchor is not included if this value is 0. + base_anchor_size: base anchor size as [height, width]. + The height and width values are normalized to the minimum dimension of the + input height and width, so that when the base anchor height equals the + base anchor width, the resulting anchor is square even if the input image + is not square. + anchor_strides: list of pairs of strides in pixels (in y and x directions + respectively). For example, setting anchor_strides=[(25, 25), (50, 50)] + means that we want the anchors corresponding to the first layer to be + strided by 25 pixels and those in the second layer to be strided by 50 + pixels in both y and x directions. If anchor_strides=None, they are set to + be the reciprocal of the corresponding feature map shapes. + anchor_offsets: list of pairs of offsets in pixels (in y and x directions + respectively). The offset specifies where we want the center of the + (0, 0)-th anchor to lie for each layer. For example, setting + anchor_offsets=[(10, 10), (20, 20)]) means that we want the + (0, 0)-th anchor of the first layer to lie at (10, 10) in pixel space + and likewise that we want the (0, 0)-th anchor of the second layer to lie + at (25, 25) in pixel space. If anchor_offsets=None, then they are set to + be half of the corresponding anchor stride. + reduce_boxes_in_lowest_layer: a boolean to indicate whether the fixed 3 + boxes per location is used in the lowest layer. + + Returns: + a MultipleGridAnchorGenerator + """ + if base_anchor_size is None: + base_anchor_size = [1.0, 1.0] + box_specs_list = [] + if scales is None or not scales: + scales = [min_scale + (max_scale - min_scale) * i / (num_layers - 1) + for i in range(num_layers)] + [1.0] + else: + # Add 1.0 to the end, which will only be used in scale_next below and used + # for computing an interpolated scale for the largest scale in the list. + scales += [1.0] + + for layer, scale, scale_next in zip( + range(num_layers), scales[:-1], scales[1:]): + layer_box_specs = [] + if layer == 0 and reduce_boxes_in_lowest_layer: + layer_box_specs = [(0.1, 1.0), (scale, 2.0), (scale, 0.5)] + else: + for aspect_ratio in aspect_ratios: + layer_box_specs.append((scale, aspect_ratio)) + # Add one more anchor, with a scale between the current scale, and the + # scale for the next layer, with a specified aspect ratio (1.0 by + # default). + if interpolated_scale_aspect_ratio > 0.0: + layer_box_specs.append((np.sqrt(scale*scale_next), + interpolated_scale_aspect_ratio)) + box_specs_list.append(layer_box_specs) + + return MultipleGridAnchorGenerator(box_specs_list, base_anchor_size, + anchor_strides, anchor_offsets) diff --git a/workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.pyc b/workspace/virtuallab/object_detection/anchor_generators/multiple_grid_anchor_generator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8abcf1d558411e1544fdcec292a7d80a00dc68b6 GIT binary patch literal 14295 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/anchor_generators/multiscale_grid_anchor_generator_test.py b/workspace/virtuallab/object_detection/anchor_generators/multiscale_grid_anchor_generator_test.py new file mode 100644 index 0000000..82aa8d1 --- /dev/null +++ b/workspace/virtuallab/object_detection/anchor_generators/multiscale_grid_anchor_generator_test.py @@ -0,0 +1,308 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for anchor_generators.multiscale_grid_anchor_generator_test.py.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.anchor_generators import multiscale_grid_anchor_generator as mg +from object_detection.utils import test_case + + +class MultiscaleGridAnchorGeneratorTest(test_case.TestCase): + + def test_construct_single_anchor(self): + def graph_fn(): + min_level = 5 + max_level = 5 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = 64 + im_width = 64 + feature_map_shape_list = [(2, 2)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + + exp_anchor_corners = [[-48, -48, 80, 80], + [-48, -16, 80, 112], + [-16, -48, 112, 80], + [-16, -16, 112, 112]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_unit_dimensions(self): + def graph_fn(): + min_level = 5 + max_level = 5 + anchor_scale = 1.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = 1 + im_width = 1 + feature_map_shape_list = [(2, 2)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + + # Positive offsets are produced. + exp_anchor_corners = [[0, 0, 32, 32], + [0, 32, 32, 64], + [32, 0, 64, 32], + [32, 32, 64, 64]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_normalized_anchors_fails_with_unit_dimensions(self): + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level=5, max_level=5, anchor_scale=1.0, aspect_ratios=[1.0], + scales_per_octave=1, normalize_coordinates=True) + with self.assertRaisesRegexp(ValueError, 'Normalized coordinates'): + anchor_generator.generate( + feature_map_shape_list=[(2, 2)], im_height=1, im_width=1) + + def test_construct_single_anchor_in_normalized_coordinates(self): + def graph_fn(): + min_level = 5 + max_level = 5 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = 64 + im_width = 128 + feature_map_shape_list = [(2, 2)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=True) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + + exp_anchor_corners = [[-48./64, -48./128, 80./64, 80./128], + [-48./64, -16./128, 80./64, 112./128], + [-16./64, -48./128, 112./64, 80./128], + [-16./64, -16./128, 112./64, 112./128]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_num_anchors_per_location(self): + min_level = 5 + max_level = 6 + anchor_scale = 4.0 + aspect_ratios = [1.0, 2.0] + scales_per_octave = 3 + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + self.assertEqual(anchor_generator.num_anchors_per_location(), [6, 6]) + + def test_construct_single_anchor_dynamic_size(self): + def graph_fn(): + min_level = 5 + max_level = 5 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = tf.constant(64) + im_width = tf.constant(64) + feature_map_shape_list = [(2, 2)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return anchor_corners + + exp_anchor_corners = [[-64, -64, 64, 64], + [-64, -32, 64, 96], + [-32, -64, 96, 64], + [-32, -32, 96, 96]] + # Add anchor offset. + anchor_offset = 2.0**5 / 2.0 + exp_anchor_corners = [ + [b + anchor_offset for b in a] for a in exp_anchor_corners + ] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_with_odd_input_dimension(self): + + def graph_fn(): + min_level = 5 + max_level = 5 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = 65 + im_width = 65 + feature_map_shape_list = [(3, 3)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate( + feature_map_shape_list, im_height=im_height, im_width=im_width) + anchor_corners = anchors_list[0].get() + return (anchor_corners,) + anchor_corners_out = self.execute(graph_fn, []) + exp_anchor_corners = [[-64, -64, 64, 64], + [-64, -32, 64, 96], + [-64, 0, 64, 128], + [-32, -64, 96, 64], + [-32, -32, 96, 96], + [-32, 0, 96, 128], + [0, -64, 128, 64], + [0, -32, 128, 96], + [0, 0, 128, 128]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_on_two_feature_maps(self): + + def graph_fn(): + min_level = 5 + max_level = 6 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = 64 + im_width = 64 + feature_map_shape_list = [(2, 2), (1, 1)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + + anchor_corners_out = np.concatenate(self.execute(graph_fn, []), axis=0) + exp_anchor_corners = [[-48, -48, 80, 80], + [-48, -16, 80, 112], + [-16, -48, 112, 80], + [-16, -16, 112, 112], + [-96, -96, 160, 160]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_with_two_scales_per_octave(self): + + def graph_fn(): + min_level = 6 + max_level = 6 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 2 + im_height = 64 + im_width = 64 + feature_map_shape_list = [(1, 1)] + + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + # There are 4 set of anchors in this configuration. The order is: + # [[2**0.0 intermediate scale + 1.0 aspect], + # [2**0.5 intermediate scale + 1.0 aspect]] + exp_anchor_corners = [[-96., -96., 160., 160.], + [-149.0193, -149.0193, 213.0193, 213.0193]] + + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchor_with_two_scales_per_octave_and_aspect(self): + def graph_fn(): + min_level = 6 + max_level = 6 + anchor_scale = 4.0 + aspect_ratios = [1.0, 2.0] + scales_per_octave = 2 + im_height = 64 + im_width = 64 + feature_map_shape_list = [(1, 1)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + # There are 4 set of anchors in this configuration. The order is: + # [[2**0.0 intermediate scale + 1.0 aspect], + # [2**0.5 intermediate scale + 1.0 aspect], + # [2**0.0 intermediate scale + 2.0 aspect], + # [2**0.5 intermediate scale + 2.0 aspect]] + + exp_anchor_corners = [[-96., -96., 160., 160.], + [-149.0193, -149.0193, 213.0193, 213.0193], + [-58.50967, -149.0193, 122.50967, 213.0193], + [-96., -224., 160., 288.]] + anchor_corners_out = self.execute(graph_fn, []) + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + def test_construct_single_anchors_on_feature_maps_with_dynamic_shape(self): + + def graph_fn(feature_map1_height, feature_map1_width, feature_map2_height, + feature_map2_width): + min_level = 5 + max_level = 6 + anchor_scale = 4.0 + aspect_ratios = [1.0] + scales_per_octave = 1 + im_height = 64 + im_width = 64 + feature_map_shape_list = [(feature_map1_height, feature_map1_width), + (feature_map2_height, feature_map2_width)] + anchor_generator = mg.MultiscaleGridAnchorGenerator( + min_level, max_level, anchor_scale, aspect_ratios, scales_per_octave, + normalize_coordinates=False) + anchors_list = anchor_generator.generate(feature_map_shape_list, + im_height=im_height, + im_width=im_width) + anchor_corners = [anchors.get() for anchors in anchors_list] + return anchor_corners + + anchor_corners_out = np.concatenate( + self.execute_cpu(graph_fn, [ + np.array(2, dtype=np.int32), + np.array(2, dtype=np.int32), + np.array(1, dtype=np.int32), + np.array(1, dtype=np.int32) + ]), + axis=0) + exp_anchor_corners = [[-48, -48, 80, 80], + [-48, -16, 80, 112], + [-16, -48, 112, 80], + [-16, -16, 112, 112], + [-96, -96, 160, 160]] + self.assertAllClose(anchor_corners_out, exp_anchor_corners) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/box_coders/__init__.py b/workspace/virtuallab/object_detection/box_coders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/virtuallab/object_detection/box_coders/__init__.pyc b/workspace/virtuallab/object_detection/box_coders/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cd73f675fc468309bb81d2f53b818b0f3ceb93a GIT binary patch literal 143 zcmZSn%*(a0Z&rLV0~9alJgH!Ge22p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CUyk}2`MIh3S@}u& zx%nxnImP-#sl};@MadcZ`AJ!+$tCeAsU<)vGe1v1DZe5<8K|hJSU)~KGcU6wK3=b& U@)m~;P-kgQsvXGaVjyM!05A?E?*IS* literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/__pycache__/faster_rcnn_box_coder.cpython-36.pyc b/workspace/virtuallab/object_detection/box_coders/__pycache__/faster_rcnn_box_coder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00ba34a59d5bb2fe75000b2d909057ac490fd83c GIT binary patch literal 3276 zcmbVO-HzMF73NTsNO866^~PzocG6CN6BTP|7x`<9G;q9eiU5IH*cSzq1jH;k61ApC zWrk}>3+krp%OVfZH|S;i7`^BN@UHDm_Z51T?+hs=j)eg22y=MO;o+IT?>nP6cX$22 ze)Z?TyxjiCd*ku;~RVT+O9f$k1n=*^+`TPtcs z?lUWLW$V0s)foDb{n+Yy;-Ad2J{CHcia$CX4ER~L;9(X?)${$Wem=^QB%5oV$C4r^ z>6q&?GMywsCCIuENRB>7k7-St{2~qGOl^ihlJ6a)hjkud z1JLJYY)s*WYf;HO!tb9E8s*c95B7v}`DbF>fDSQBii=};sx7zz4H-{YTR zqj)yrZ}C~CCj=Z5ArzR9Rl(I^)1g;j%jdm17w4ahxRyDE4zg4p@hFp;16q706{a*b z8Nj6;|9CS&!^ope3L;!Klj&WZU8qp<$#j;;DNrNwNtOa8VK#*sX?~%Jm7kp)_l}Qv zCEq3$;)`lZ*HlRqwg#|hl#N8D<=%%{OGT+3DwU~nFL-VT5awu^jLO~T++~YQq5fj2 z4zm4s(2ujJ?4M_6{V5JR(S0yY3Khov?Cczj3xG-@UY7PZVATDQDM5h(OoENOH(Qoo z5KPiZ9t6KacI`u0p2PN;kA8V)%R#Q=Ls}d)dQvxFqGd-6z_16Z3EfP(%T#-<8;9D3 zS+zcyD zem*pXi;MckNRsMMQEP$zy1EkFtE!1oxsKREHJrEv#ZVv&%i=6C*NVA~Koev+lf15! zYf4plzx5TkS^Me{9?ASdr5ipAp378&TuROelW9&Q8Pp%G3o$JRLn0Z`jhHFux}f%% zspW>=RMTrF84_I+z^(})*MxwsTk^5YgQ}#X;BM}s0GE(~lrG(ezkmG6r@tGNc0K|F zv$68&iOBFpx&PA|&#Vx!uzDDAl@-TH0m*6&Id zmu!x~7U)ZK(&Gy5#M}z=ZcuIW`|xNEVRiP|zU?vdvpcNA_Sl}?B58h?x3}=2PO@Rc zOUUD*8#eq0%6Hk2<1Y+)gDU(L154JdAOw$q_Zq>kSt}PzAPWeFlRpJtTwDBtPXPGSu|d;R=dcEh{AzR9Oy*g&!M%t0o3o*`u^IFn44=q z^n<*0?$XTBz2Zi(U)(GXDyA5&v@ZYg5$bXzfw}~B8n*^n6{ZPkTiAd|b4LT){|jdS zZ(@88`z}&LL=#cvH*i<`I74-l!~w(@o!b3wThURgmmx~`0+%lEt6m|O!FEF~W{L<> zx(_O@t3z1bmkh#(?aj*7ZTjX8iC0OyM&d^#eoo?NBz{Ta7bJd4;wL12OoBs{&T=|Q zOJ`x^aw-<2i^Yq0kIeCWj&CZw`}_7G`v%{Hazg$3w;k_Qk}ex`+jL{4Vk(26^n(C* z;069g1Z5`(zPu1g{l*P~C<}u??a^GlnZmuU@};6{L%mL-Pr~r>8$q{{q8p4N%w6a) z$M*L+j>nC^yQAL3tMXP==|=by-ou}QnaF!jPs&!MmiE(=>V#hY?v0FKB^8x1b4uzK hCG&7pr@*W-Lu{K<>AhdA^8-33Gcm{RP_eL`#@8TKZg~Iz literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/__pycache__/keypoint_box_coder.cpython-36.pyc b/workspace/virtuallab/object_detection/box_coders/__pycache__/keypoint_box_coder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e2ee2675261e7abb582874ebccc1ec7e9224732 GIT binary patch literal 5040 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 BdGi1O literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/__pycache__/mean_stddev_box_coder.cpython-36.pyc b/workspace/virtuallab/object_detection/box_coders/__pycache__/mean_stddev_box_coder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdc3561becb2f4a04ee2eb02d16965d5d5c9c6de GIT binary patch literal 2386 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 F{|#U(Bgg;% literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.py b/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.py new file mode 100644 index 0000000..e06c1b1 --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.py @@ -0,0 +1,118 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Faster RCNN box coder. + +Faster RCNN box coder follows the coding schema described below: + ty = (y - ya) / ha + tx = (x - xa) / wa + th = log(h / ha) + tw = log(w / wa) + where x, y, w, h denote the box's center coordinates, width and height + respectively. Similarly, xa, ya, wa, ha denote the anchor's center + coordinates, width and height. tx, ty, tw and th denote the anchor-encoded + center, width and height respectively. + + See http://arxiv.org/abs/1506.01497 for details. +""" + +import tensorflow.compat.v1 as tf + +from object_detection.core import box_coder +from object_detection.core import box_list + +EPSILON = 1e-8 + + +class FasterRcnnBoxCoder(box_coder.BoxCoder): + """Faster RCNN box coder.""" + + def __init__(self, scale_factors=None): + """Constructor for FasterRcnnBoxCoder. + + Args: + scale_factors: List of 4 positive scalars to scale ty, tx, th and tw. + If set to None, does not perform scaling. For Faster RCNN, + the open-source implementation recommends using [10.0, 10.0, 5.0, 5.0]. + """ + if scale_factors: + assert len(scale_factors) == 4 + for scalar in scale_factors: + assert scalar > 0 + self._scale_factors = scale_factors + + @property + def code_size(self): + return 4 + + def _encode(self, boxes, anchors): + """Encode a box collection with respect to anchor collection. + + Args: + boxes: BoxList holding N boxes to be encoded. + anchors: BoxList of anchors. + + Returns: + a tensor representing N anchor-encoded boxes of the format + [ty, tx, th, tw]. + """ + # Convert anchors to the center coordinate representation. + ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes() + ycenter, xcenter, h, w = boxes.get_center_coordinates_and_sizes() + # Avoid NaN in division and log below. + ha += EPSILON + wa += EPSILON + h += EPSILON + w += EPSILON + + tx = (xcenter - xcenter_a) / wa + ty = (ycenter - ycenter_a) / ha + tw = tf.log(w / wa) + th = tf.log(h / ha) + # Scales location targets as used in paper for joint training. + if self._scale_factors: + ty *= self._scale_factors[0] + tx *= self._scale_factors[1] + th *= self._scale_factors[2] + tw *= self._scale_factors[3] + return tf.transpose(tf.stack([ty, tx, th, tw])) + + def _decode(self, rel_codes, anchors): + """Decode relative codes to boxes. + + Args: + rel_codes: a tensor representing N anchor-encoded boxes. + anchors: BoxList of anchors. + + Returns: + boxes: BoxList holding N bounding boxes. + """ + ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes() + + ty, tx, th, tw = tf.unstack(tf.transpose(rel_codes)) + if self._scale_factors: + ty /= self._scale_factors[0] + tx /= self._scale_factors[1] + th /= self._scale_factors[2] + tw /= self._scale_factors[3] + w = tf.exp(tw) * wa + h = tf.exp(th) * ha + ycenter = ty * ha + ycenter_a + xcenter = tx * wa + xcenter_a + ymin = ycenter - h / 2. + xmin = xcenter - w / 2. + ymax = ycenter + h / 2. + xmax = xcenter + w / 2. + return box_list.BoxList(tf.transpose(tf.stack([ymin, xmin, ymax, xmax]))) diff --git a/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.pyc b/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9def75cf84987d02d25c5f9f51005e994a9b7dad GIT binary patch literal 3866 zcmcIn-EP~)5*|{rY%8{tI!W#HcY(I3dg@3v&|milZ4w74P^jn77sYW}5Hz(C=}@G~ zT}M(O-L$NQ9= z=xm+(7+$3xh`uNK_eqnz9k5O`+mW%lRKd+|SDh9U6&F2Swwukz>Q%qUvSMtM zO*C7k`9PUC(Zfjfw28~~RQJ@WhRI=5DLYjksn%3oSJUVX)lo^r$_XncP)ErgQN2 z{yXjcgSXy)7hkEwYBowU({85!#nWx!Qfp-3Ik)+y*{@Rc@?N zaNKIJlV>3w3%7R63o$47y@}WS3u5L`d5f$xzQ@WQ-Sx@5MAoD4Jes`12DiT6rMXWt zY{{p)buzD2!{59{U*T^U=6@B%V8WkpGl!--Df;NRScF16aIAq#!yYi*fXMJU~Wz%kv>pj&g zv{AsJ8tD>S8j1%HudQw`Hz@SoTe@I&79*WsH^q4wYn2X1nH~aSkxh#nn2L)bx;>-L z4O95e!G3#xPdWKEHzB@uTS_@LDPHQf_$ycz8GLQw_funZ$v%(DvM5CrJ`@c%MZ&AI zzTL!}hsLb{0P%~;7RE?~IYO(G6hqxPD^5E@Krb_$;`9uh4S{Q>ZjpDEKsKGeB(bmr zR>P&bJ(^lZcNnI5YQyj!XqzSktu?$|Z_~Tx#mtJuEBNBA0#L^-MqXDA?>5F(0q^#y z?30Pfqzp~^t$q(3!|RhDbF?qw<&|ZF8`ua?eJ1Jrt2)hVG>2TFd4q9(U%yK8HJY!} zyh*ba2LHP-nl)Stw(G97PP3+K!ETLab=rl|)%gXQtg7|mbT0vyY8lrL}*;GYU8Ot(<6BGcjtV;PzE0@ZVzZVvdp)^3GL1iR9VRKSs zQtPBdf{yTVs#TS~7u0orADD$4Q0b}_0&UOBe3>^QWp!>q;!=;WKX8i4*sUflsskSf zOTrn`nlnJeHH@D-QeY&FaG_Z+76GA@@tQJ-Qif1#6~ZcrfwrNmHmCy2G8v*i3iga; z%cFlDfA;woUBn%;`i}C&AmC6-4Y6gE8)P?aCHgjs@3t7FLJ-l(ro~qk0+~0hR*s}M zS@w9HB$DOEk<6+rrCr$p!mtuKb~~H6pQ-z)l*v;eC`To9AN)f!je?+!U2oTKdh+LQ zd0XCP@3Oz{JtKctJR$F8*zsf`@4*wui-X~Y$FjymcJO9`KS*RpuOEe*j!mG~AxxOM zOL`rG`QUe@0KE>82bj9f0`xjWR$zKzH6(>5uLfRU0WB-lx~Nv50_)%&EhK{-u##7T zDH6p_McNH6`&W187lqRos@^3bv>V%*Kl8xY&U~AY_|I0W)AEvVis_gG!);p9TNo}i z;#b7*77ABSgDx2g8Okd692uD+kD)Ov<*ox#Mt_5B|1+bXkT%Y9$sl4;RaTtnuRI;F z4$mhot_Z;e7F;xi2j{q(fN6m+O}NO^){{}d%n*|sE@1_iplC7l93$D;G8O?}?gAOS z!mX<;_zo7lz~XrpFR^%$#d9pKvDjg;3t_oPPKRkOjme@h9Y&K%6HT5T33~#9udoG2 z!sf33s&}7spdg5N|F0{NtS$n1G}K{e`4SL@xL2Ix-WE#W!mwAwX!Ftd-W71jWY(y} zRjRbpfM*3Sv*5$sKcKY!e|MIEli}-l<*w538ve%C#rnEx2^ikQfaTBTN=G06)oSB5 zH;Qcg-htevTu)Nsy#ogx{=$E~K4Dg=0}PO$C)i-0YY>2tT=r!jPwkYl)Q9d9e9S;| Or}ek}OWq~FQTr2Kj-+h> literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder_test.py b/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder_test.py new file mode 100644 index 0000000..1cd4827 --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/faster_rcnn_box_coder_test.py @@ -0,0 +1,113 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.box_coder.faster_rcnn_box_coder.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.box_coders import faster_rcnn_box_coder +from object_detection.core import box_list +from object_detection.utils import test_case + + +class FasterRcnnBoxCoderTest(test_case.TestCase): + + def test_get_correct_relative_codes_after_encoding(self): + boxes = np.array([[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]], + np.float32) + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + expected_rel_codes = [[-0.5, -0.416666, -0.405465, -0.182321], + [-0.083333, -0.222222, -0.693147, -1.098612]] + def graph_fn(boxes, anchors): + boxes = box_list.BoxList(boxes) + anchors = box_list.BoxList(anchors) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def test_get_correct_relative_codes_after_encoding_with_scaling(self): + boxes = np.array([[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]], + np.float32) + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + expected_rel_codes = [[-1., -1.25, -1.62186, -0.911608], + [-0.166667, -0.666667, -2.772588, -5.493062]] + def graph_fn(boxes, anchors): + scale_factors = [2, 3, 4, 5] + boxes = box_list.BoxList(boxes) + anchors = box_list.BoxList(anchors) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def test_get_correct_boxes_after_decoding(self): + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + rel_codes = np.array([[-0.5, -0.416666, -0.405465, -0.182321], + [-0.083333, -0.222222, -0.693147, -1.098612]], + np.float32) + expected_boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + def graph_fn(rel_codes, anchors): + anchors = box_list.BoxList(anchors) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + boxes = coder.decode(rel_codes, anchors) + return boxes.get() + boxes_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(boxes_out, expected_boxes, rtol=1e-04, + atol=1e-04) + + def test_get_correct_boxes_after_decoding_with_scaling(self): + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + rel_codes = np.array([[-1., -1.25, -1.62186, -0.911608], + [-0.166667, -0.666667, -2.772588, -5.493062]], + np.float32) + expected_boxes = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + def graph_fn(rel_codes, anchors): + scale_factors = [2, 3, 4, 5] + anchors = box_list.BoxList(anchors) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors).get() + return boxes + boxes_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(expected_boxes, boxes_out, rtol=1e-04, + atol=1e-04) + + def test_very_small_Width_nan_after_encoding(self): + boxes = np.array([[10.0, 10.0, 10.0000001, 20.0]], np.float32) + anchors = np.array([[15.0, 12.0, 30.0, 18.0]], np.float32) + expected_rel_codes = [[-0.833333, 0., -21.128731, 0.510826]] + def graph_fn(boxes, anchors): + boxes = box_list.BoxList(boxes) + anchors = box_list.BoxList(anchors) + coder = faster_rcnn_box_coder.FasterRcnnBoxCoder() + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.py b/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.py new file mode 100644 index 0000000..7bb4bf8 --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.py @@ -0,0 +1,173 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Keypoint box coder. + +The keypoint box coder follows the coding schema described below (this is +similar to the FasterRcnnBoxCoder, except that it encodes keypoints in addition +to box coordinates): + ty = (y - ya) / ha + tx = (x - xa) / wa + th = log(h / ha) + tw = log(w / wa) + tky0 = (ky0 - ya) / ha + tkx0 = (kx0 - xa) / wa + tky1 = (ky1 - ya) / ha + tkx1 = (kx1 - xa) / wa + ... + where x, y, w, h denote the box's center coordinates, width and height + respectively. Similarly, xa, ya, wa, ha denote the anchor's center + coordinates, width and height. tx, ty, tw and th denote the anchor-encoded + center, width and height respectively. ky0, kx0, ky1, kx1, ... denote the + keypoints' coordinates, and tky0, tkx0, tky1, tkx1, ... denote the + anchor-encoded keypoint coordinates. +""" + +import tensorflow.compat.v1 as tf + +from object_detection.core import box_coder +from object_detection.core import box_list +from object_detection.core import standard_fields as fields + +EPSILON = 1e-8 + + +class KeypointBoxCoder(box_coder.BoxCoder): + """Keypoint box coder.""" + + def __init__(self, num_keypoints, scale_factors=None): + """Constructor for KeypointBoxCoder. + + Args: + num_keypoints: Number of keypoints to encode/decode. + scale_factors: List of 4 positive scalars to scale ty, tx, th and tw. + In addition to scaling ty and tx, the first 2 scalars are used to scale + the y and x coordinates of the keypoints as well. If set to None, does + not perform scaling. + """ + self._num_keypoints = num_keypoints + + if scale_factors: + assert len(scale_factors) == 4 + for scalar in scale_factors: + assert scalar > 0 + self._scale_factors = scale_factors + self._keypoint_scale_factors = None + if scale_factors is not None: + self._keypoint_scale_factors = tf.expand_dims( + tf.tile([ + tf.cast(scale_factors[0], dtype=tf.float32), + tf.cast(scale_factors[1], dtype=tf.float32) + ], [num_keypoints]), 1) + + @property + def code_size(self): + return 4 + self._num_keypoints * 2 + + def _encode(self, boxes, anchors): + """Encode a box and keypoint collection with respect to anchor collection. + + Args: + boxes: BoxList holding N boxes and keypoints to be encoded. Boxes are + tensors with the shape [N, 4], and keypoints are tensors with the shape + [N, num_keypoints, 2]. + anchors: BoxList of anchors. + + Returns: + a tensor representing N anchor-encoded boxes of the format + [ty, tx, th, tw, tky0, tkx0, tky1, tkx1, ...] where tky0 and tkx0 + represent the y and x coordinates of the first keypoint, tky1 and tkx1 + represent the y and x coordinates of the second keypoint, and so on. + """ + # Convert anchors to the center coordinate representation. + ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes() + ycenter, xcenter, h, w = boxes.get_center_coordinates_and_sizes() + keypoints = boxes.get_field(fields.BoxListFields.keypoints) + keypoints = tf.transpose(tf.reshape(keypoints, + [-1, self._num_keypoints * 2])) + num_boxes = boxes.num_boxes() + + # Avoid NaN in division and log below. + ha += EPSILON + wa += EPSILON + h += EPSILON + w += EPSILON + + tx = (xcenter - xcenter_a) / wa + ty = (ycenter - ycenter_a) / ha + tw = tf.log(w / wa) + th = tf.log(h / ha) + + tiled_anchor_centers = tf.tile( + tf.stack([ycenter_a, xcenter_a]), [self._num_keypoints, 1]) + tiled_anchor_sizes = tf.tile( + tf.stack([ha, wa]), [self._num_keypoints, 1]) + tkeypoints = (keypoints - tiled_anchor_centers) / tiled_anchor_sizes + + # Scales location targets as used in paper for joint training. + if self._scale_factors: + ty *= self._scale_factors[0] + tx *= self._scale_factors[1] + th *= self._scale_factors[2] + tw *= self._scale_factors[3] + tkeypoints *= tf.tile(self._keypoint_scale_factors, [1, num_boxes]) + + tboxes = tf.stack([ty, tx, th, tw]) + return tf.transpose(tf.concat([tboxes, tkeypoints], 0)) + + def _decode(self, rel_codes, anchors): + """Decode relative codes to boxes and keypoints. + + Args: + rel_codes: a tensor with shape [N, 4 + 2 * num_keypoints] representing N + anchor-encoded boxes and keypoints + anchors: BoxList of anchors. + + Returns: + boxes: BoxList holding N bounding boxes and keypoints. + """ + ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes() + + num_codes = tf.shape(rel_codes)[0] + result = tf.unstack(tf.transpose(rel_codes)) + ty, tx, th, tw = result[:4] + tkeypoints = result[4:] + if self._scale_factors: + ty /= self._scale_factors[0] + tx /= self._scale_factors[1] + th /= self._scale_factors[2] + tw /= self._scale_factors[3] + tkeypoints /= tf.tile(self._keypoint_scale_factors, [1, num_codes]) + + w = tf.exp(tw) * wa + h = tf.exp(th) * ha + ycenter = ty * ha + ycenter_a + xcenter = tx * wa + xcenter_a + ymin = ycenter - h / 2. + xmin = xcenter - w / 2. + ymax = ycenter + h / 2. + xmax = xcenter + w / 2. + decoded_boxes_keypoints = box_list.BoxList( + tf.transpose(tf.stack([ymin, xmin, ymax, xmax]))) + + tiled_anchor_centers = tf.tile( + tf.stack([ycenter_a, xcenter_a]), [self._num_keypoints, 1]) + tiled_anchor_sizes = tf.tile( + tf.stack([ha, wa]), [self._num_keypoints, 1]) + keypoints = tkeypoints * tiled_anchor_sizes + tiled_anchor_centers + keypoints = tf.reshape(tf.transpose(keypoints), + [-1, self._num_keypoints, 2]) + decoded_boxes_keypoints.add_field(fields.BoxListFields.keypoints, keypoints) + return decoded_boxes_keypoints diff --git a/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.pyc b/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf64d09130f6b332c1f2fe05a6d2b98f996307e3 GIT binary patch literal 5876 zcmcIo(QX^Z6`ds|N}^=Rwq#3c+;-3;HJ!+mNf{V^uAwPV5c|G{pV(ImL^@N57WMG zq)n66tJPn%j6Qv>LLa5=cG|Prp&F)1_gLFT%XDJhG`5i?ho-3y4NP>+wGyinTeV3i zX~&s%skHhmw$5Z>quYIw_Mfu1Ep7S@bLLPTJDoUfx{SgW{liUH$IWKqlC)ceA0KF% zq5s$!dvCX@wHxT4>)Jry*Ms<;-qx*{%088SDEp%9iLwP{J3X$o#OxmJdbz3xRWBCR z>EHny^Jm)UY2R1iXWH-SV8^%Ed98)7=(A!Xl6t+4&U-DB8QtH~gDu_L(k-O2n>r)e z!btC0-7sC`3<)oW49leHS~~7Fb;~5jEeA)Lv1g{?l9#4EsOx>7_cpxtV>rjt!_(qa z75?LHqm^bw7jW`lJE9Jl-{)Nw8Ex-nG+$ETVNcM$S&F89i zVx8Bla4R_k5@*fmC^7A(J>LDpgTH?}c=nOIj>_>C8!!1{_m@!lgsp!DVX*tLQz1*L zIm=D|O~Bo6P%g>##cM@1%PeEt0|Ho>SK)rGLMN*bCLYAtZyatQzj0k}>`UwC z7YN55X-{wks>%A6Mn)ec8G3nGc#Z)a=N6coH!*o^Kr{ffZ%a%saDx%Dsu;75t?rq2 zyRM%fX=?z&soqPwW=l6yV+(fx41H!YWVJJn&BvZ_T_t#wn}uk)!I??8bM|VBR4zkA zQ(1Ogjcot}t3I*TWQ6r+S(awrdMdGw{uz*4r>Ak;(X!_3544RuO{<@Vifr5&?3Hcuw0B{2FzEbM9GenpXPzTwUJYl|28?cvuBjn9DC2DwZ_Co-B5hgAE>^}Wcz_92*G07?672z^Gm0_!sX~lp zc`d2YjcH2>>}t!K9Odu^;<$-8R%OzcRrcp#xSW>(l$(zc;SgP7MJ%%$DDZZDtN{K# zMs&lKv0|1AyshSOu;B_D4wv!}AEPh8F4GN9-n)1>_A{|2RemO*U!yOKz@s)soP#x1 zr+OxbJRJ*95#z?&0&vxC`(1-W4xlnW))1LIeNR|i0qNLP26zn67f5cUZ8@gyu_GKR zC%Y18J~TPLH|uOflWYPC@Fs!>TIafaprLW+S+}6CIPVTQIzyTOsw9oQyisw?@gvWnpBNtVxX8~}a+VWz z;hB!-CZ5HvJW0LCPpUMLfzvAYQ}q8`#N!!kJlE3S&e++`|LV+wkSA7-pAMVqw40L? z4{{07M<_{XDkub|0@6hip2asZ@}9P4mK0e{E=PY5GlWS@Br zfp0uA8w&kV<+8YIaVRjtKA0x1AVFbq&eb3jB-PJ;x&Qo&FZVo2rpF;kNSGDp%f@L) zmI$lVF%pcv5xW{OH6)gc;f1e!=}0ewe2}rc;l9NiMHB-^mBO`)KfPEY$StLu+wpLv zS|WQPc`5upgfwz}|1x-xPk4v*iWCz zWdgw6j^(o8|MCFG$*LDfEVjcHEQfY-Ck0u=V-gUyoZu(@KCTMyP1DVbOH@%1li#M7 zC}WB&bJHAEE*SQ^NJ<8DVLnO7=Uw6ED~C*a@`x>03__5Hasv1<9)3QSyLyeF65gf| z)a40R@*p5~j$0wI^DWj3fD%+aiqaQu3?aRX@e_Y+2sKUUC%nTd;yC_+@+=T;P`^pR z&0iBl6#S)GF*D)Yw0MW&U5X!5+=UQtgngNF!8)+QdE15eX|hGJO;M-dWgvW?;ynuD zVR#Qht}cTP{ohSA{^U!Yr>`L9Cz_Z zW-yAHX(Nh4&P6CR#5q69QrutNAQZBE#JVRC%;to6LzlUb%Zf8-86U1}rKQc4nS~E( zvhbck=lM~NinxyU>W#E>7Q6b(ot)SCCRF-z$1iDyefd&}R+$M=p=AArByqnse}IxC l3d{K^u40))^#2*G^z)&gf{(b`?Gi+#bhC6lz_(N>{~O7gq;>!R literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder_test.py b/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder_test.py new file mode 100644 index 0000000..5748255 --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/keypoint_box_coder_test.py @@ -0,0 +1,151 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.box_coder.keypoint_box_coder.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.box_coders import keypoint_box_coder +from object_detection.core import box_list +from object_detection.core import standard_fields as fields +from object_detection.utils import test_case + + +class KeypointBoxCoderTest(test_case.TestCase): + + def test_get_correct_relative_codes_after_encoding(self): + boxes = np.array([[10., 10., 20., 15.], + [0.2, 0.1, 0.5, 0.4]], np.float32) + keypoints = np.array([[[15., 12.], [10., 15.]], + [[0.5, 0.3], [0.2, 0.4]]], np.float32) + num_keypoints = len(keypoints[0]) + anchors = np.array([[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]], np.float32) + expected_rel_codes = [ + [-0.5, -0.416666, -0.405465, -0.182321, + -0.5, -0.5, -0.833333, 0.], + [-0.083333, -0.222222, -0.693147, -1.098612, + 0.166667, -0.166667, -0.333333, -0.055556] + ] + def graph_fn(boxes, keypoints, anchors): + boxes = box_list.BoxList(boxes) + boxes.add_field(fields.BoxListFields.keypoints, keypoints) + anchors = box_list.BoxList(anchors) + coder = keypoint_box_coder.KeypointBoxCoder(num_keypoints) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, keypoints, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def test_get_correct_relative_codes_after_encoding_with_scaling(self): + boxes = np.array([[10., 10., 20., 15.], + [0.2, 0.1, 0.5, 0.4]], np.float32) + keypoints = np.array([[[15., 12.], [10., 15.]], + [[0.5, 0.3], [0.2, 0.4]]], np.float32) + num_keypoints = len(keypoints[0]) + anchors = np.array([[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]], np.float32) + expected_rel_codes = [ + [-1., -1.25, -1.62186, -0.911608, + -1.0, -1.5, -1.666667, 0.], + [-0.166667, -0.666667, -2.772588, -5.493062, + 0.333333, -0.5, -0.666667, -0.166667] + ] + def graph_fn(boxes, keypoints, anchors): + scale_factors = [2, 3, 4, 5] + boxes = box_list.BoxList(boxes) + boxes.add_field(fields.BoxListFields.keypoints, keypoints) + anchors = box_list.BoxList(anchors) + coder = keypoint_box_coder.KeypointBoxCoder( + num_keypoints, scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, keypoints, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def test_get_correct_boxes_after_decoding(self): + anchors = np.array([[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]], np.float32) + rel_codes = np.array([ + [-0.5, -0.416666, -0.405465, -0.182321, + -0.5, -0.5, -0.833333, 0.], + [-0.083333, -0.222222, -0.693147, -1.098612, + 0.166667, -0.166667, -0.333333, -0.055556] + ], np.float32) + expected_boxes = [[10., 10., 20., 15.], + [0.2, 0.1, 0.5, 0.4]] + expected_keypoints = [[[15., 12.], [10., 15.]], + [[0.5, 0.3], [0.2, 0.4]]] + num_keypoints = len(expected_keypoints[0]) + def graph_fn(rel_codes, anchors): + anchors = box_list.BoxList(anchors) + coder = keypoint_box_coder.KeypointBoxCoder(num_keypoints) + boxes = coder.decode(rel_codes, anchors) + return boxes.get(), boxes.get_field(fields.BoxListFields.keypoints) + boxes_out, keypoints_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(keypoints_out, expected_keypoints, rtol=1e-04, + atol=1e-04) + self.assertAllClose(boxes_out, expected_boxes, rtol=1e-04, + atol=1e-04) + + def test_get_correct_boxes_after_decoding_with_scaling(self): + anchors = np.array([[15., 12., 30., 18.], + [0.1, 0.0, 0.7, 0.9]], np.float32) + rel_codes = np.array([ + [-1., -1.25, -1.62186, -0.911608, + -1.0, -1.5, -1.666667, 0.], + [-0.166667, -0.666667, -2.772588, -5.493062, + 0.333333, -0.5, -0.666667, -0.166667] + ], np.float32) + expected_boxes = [[10., 10., 20., 15.], + [0.2, 0.1, 0.5, 0.4]] + expected_keypoints = [[[15., 12.], [10., 15.]], + [[0.5, 0.3], [0.2, 0.4]]] + num_keypoints = len(expected_keypoints[0]) + def graph_fn(rel_codes, anchors): + scale_factors = [2, 3, 4, 5] + anchors = box_list.BoxList(anchors) + coder = keypoint_box_coder.KeypointBoxCoder( + num_keypoints, scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors) + return boxes.get(), boxes.get_field(fields.BoxListFields.keypoints) + boxes_out, keypoints_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(keypoints_out, expected_keypoints, rtol=1e-04, + atol=1e-04) + self.assertAllClose(boxes_out, expected_boxes, rtol=1e-04, + atol=1e-04) + + def test_very_small_width_nan_after_encoding(self): + boxes = np.array([[10., 10., 10.0000001, 20.]], np.float32) + keypoints = np.array([[[10., 10.], [10.0000001, 20.]]], np.float32) + anchors = np.array([[15., 12., 30., 18.]], np.float32) + expected_rel_codes = [[-0.833333, 0., -21.128731, 0.510826, + -0.833333, -0.833333, -0.833333, 0.833333]] + def graph_fn(boxes, keypoints, anchors): + boxes = box_list.BoxList(boxes) + boxes.add_field(fields.BoxListFields.keypoints, keypoints) + anchors = box_list.BoxList(anchors) + coder = keypoint_box_coder.KeypointBoxCoder(2) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, keypoints, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.py b/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.py new file mode 100644 index 0000000..256f53f --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.py @@ -0,0 +1,79 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Mean stddev box coder. + +This box coder use the following coding schema to encode boxes: +rel_code = (box_corner - anchor_corner_mean) / anchor_corner_stddev. +""" +from object_detection.core import box_coder +from object_detection.core import box_list + + +class MeanStddevBoxCoder(box_coder.BoxCoder): + """Mean stddev box coder.""" + + def __init__(self, stddev=0.01): + """Constructor for MeanStddevBoxCoder. + + Args: + stddev: The standard deviation used to encode and decode boxes. + """ + self._stddev = stddev + + @property + def code_size(self): + return 4 + + def _encode(self, boxes, anchors): + """Encode a box collection with respect to anchor collection. + + Args: + boxes: BoxList holding N boxes to be encoded. + anchors: BoxList of N anchors. + + Returns: + a tensor representing N anchor-encoded boxes + + Raises: + ValueError: if the anchors still have deprecated stddev field. + """ + box_corners = boxes.get() + if anchors.has_field('stddev'): + raise ValueError("'stddev' is a parameter of MeanStddevBoxCoder and " + "should not be specified in the box list.") + means = anchors.get() + return (box_corners - means) / self._stddev + + def _decode(self, rel_codes, anchors): + """Decode. + + Args: + rel_codes: a tensor representing N anchor-encoded boxes. + anchors: BoxList of anchors. + + Returns: + boxes: BoxList holding N bounding boxes + + Raises: + ValueError: if the anchors still have deprecated stddev field and expects + the decode method to use stddev value from that field. + """ + means = anchors.get() + if anchors.has_field('stddev'): + raise ValueError("'stddev' is a parameter of MeanStddevBoxCoder and " + "should not be specified in the box list.") + box_corners = rel_codes * self._stddev + means + return box_list.BoxList(box_corners) diff --git a/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.pyc b/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b1d9534a6eea3e24d778b9c3305c86366756d3c GIT binary patch literal 2770 zcmcgu&2A$_5bhZ}-mFPB3(E2%ap(im4oGX?IE3XdA%X)~V!T4am(gUVof&I9ZFRS^ z$&$T5&O87TuYklO@eDiwd{y0!!)~+)DPg3p>A$J2`s%Cd>HN9f|NZA*vO z_5h+3Vgeb7G8RoF=CPQ>BBF7J#~m^0VB8fd7PGFHVqrsE2=S8;-%mC~*%RaB4k`N; z;v1E>(z>!#C-O+2%0idQ4Ep^iRc+T3a%q)xm6B84H2P`X9@9p8*`iYOTskdPOW)Er z%I@`zYBC;wEE7zjZ(3#KBbm2FrA-)Ta~%J^9KJB??HTmzzwjgx%yf4lydK!+r#8b> zQ(NcW2)>Njm3@7BK#p7j7-M-K&3*)-03bktj021p5n-mf1R)`XA%N;qf6@!^P40+v zob*V^n-JFncl^WdZ(siL?BdgcPFyvLn%#ytkHonU8AorN(6T*)IMA(iW?49GfUuFI z`=5>=A`;K%<`~#gPlllF$tMVlb$MInrj!U{ox57M6tVJ3Sg?-8b%X}2W#o}0He?=j zHp+F$GSy5SyUUO!m=Y}PA$r3~&((0IkA`yq-PoZ%nyJENrE++|fZ=7d?2w`y^kmf$s2JIu(5oVdKpQ-mq zHyaoi9rQQQXvhWd!(BwFmms^8fa=h)m8Z-W=J zj(9LmNVWYCejWOR%0n79jR#gftz9LJvI~qU!ak#rkgFZnkOinCD<$&cYh;V8bi>&@ z^65%ij#S9t(o;hQy;r`{Q#o4AhBH#-mZrVrBnp9QEf{Rn0v4*)QH@xA^vIWp(jV#> zo#wTrdf_?!UEVC!p)uO*$$H8a$9rc%@w#bbm7gf^7|M&>f#9CBa$2h<*qu^XEDEG} z=ocSKRI*$ya+A+NP9x#Q%LxX*psmu&rj)IA1P1|JLvtzXmQ4YOl8VhBA;LKd)Uk40 znyTDpY=rA$cI`$=n-VgKN$RM_v2c`g!8iAspAALP@-E65dn-xD+3rI@`Yl;3wzs04 zXeZu^UZ(a}SY8wMU#ZO{4piO+vEO>$6(}aN4Wc;MAK3jZ*!=|;qid+Tx;a3+|3ar% zu{Pj*J!fB$8q1cmgm>IHU72y}l(N_c`b(x+DAOQorMZOD6cL(^>QYjXMj|q8;nDFlq>%29K8K%b`#ZPu|tR?Vz J(f#Ot=Wnt?-6{Y8 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder_test.py b/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder_test.py new file mode 100644 index 0000000..d94fff1 --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/mean_stddev_box_coder_test.py @@ -0,0 +1,61 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.box_coder.mean_stddev_boxcoder.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.box_coders import mean_stddev_box_coder +from object_detection.core import box_list +from object_detection.utils import test_case + + +class MeanStddevBoxCoderTest(test_case.TestCase): + + def testGetCorrectRelativeCodesAfterEncoding(self): + boxes = np.array([[0.0, 0.0, 0.5, 0.5], [0.0, 0.0, 0.5, 0.5]], np.float32) + anchors = np.array([[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 0.8]], np.float32) + expected_rel_codes = [[0.0, 0.0, 0.0, 0.0], [-5.0, -5.0, -5.0, -3.0]] + + def graph_fn(boxes, anchors): + anchors = box_list.BoxList(anchors) + boxes = box_list.BoxList(boxes) + coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def testGetCorrectBoxesAfterDecoding(self): + rel_codes = np.array([[0.0, 0.0, 0.0, 0.0], [-5.0, -5.0, -5.0, -3.0]], + np.float32) + expected_box_corners = [[0.0, 0.0, 0.5, 0.5], [0.0, 0.0, 0.5, 0.5]] + anchors = np.array([[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 0.8]], np.float32) + + def graph_fn(rel_codes, anchors): + anchors = box_list.BoxList(anchors) + coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + decoded_boxes = coder.decode(rel_codes, anchors).get() + return decoded_boxes + + decoded_boxes_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(decoded_boxes_out, expected_box_corners, rtol=1e-04, + atol=1e-04) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/box_coders/square_box_coder.py b/workspace/virtuallab/object_detection/box_coders/square_box_coder.py new file mode 100644 index 0000000..859320f --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/square_box_coder.py @@ -0,0 +1,126 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Square box coder. + +Square box coder follows the coding schema described below: +l = sqrt(h * w) +la = sqrt(ha * wa) +ty = (y - ya) / la +tx = (x - xa) / la +tl = log(l / la) +where x, y, w, h denote the box's center coordinates, width, and height, +respectively. Similarly, xa, ya, wa, ha denote the anchor's center +coordinates, width and height. tx, ty, tl denote the anchor-encoded +center, and length, respectively. Because the encoded box is a square, only +one length is encoded. + +This has shown to provide performance improvements over the Faster RCNN box +coder when the objects being detected tend to be square (e.g. faces) and when +the input images are not distorted via resizing. +""" + +import tensorflow.compat.v1 as tf + +from object_detection.core import box_coder +from object_detection.core import box_list + +EPSILON = 1e-8 + + +class SquareBoxCoder(box_coder.BoxCoder): + """Encodes a 3-scalar representation of a square box.""" + + def __init__(self, scale_factors=None): + """Constructor for SquareBoxCoder. + + Args: + scale_factors: List of 3 positive scalars to scale ty, tx, and tl. + If set to None, does not perform scaling. For faster RCNN, + the open-source implementation recommends using [10.0, 10.0, 5.0]. + + Raises: + ValueError: If scale_factors is not length 3 or contains values less than + or equal to 0. + """ + if scale_factors: + if len(scale_factors) != 3: + raise ValueError('The argument scale_factors must be a list of length ' + '3.') + if any(scalar <= 0 for scalar in scale_factors): + raise ValueError('The values in scale_factors must all be greater ' + 'than 0.') + self._scale_factors = scale_factors + + @property + def code_size(self): + return 3 + + def _encode(self, boxes, anchors): + """Encodes a box collection with respect to an anchor collection. + + Args: + boxes: BoxList holding N boxes to be encoded. + anchors: BoxList of anchors. + + Returns: + a tensor representing N anchor-encoded boxes of the format + [ty, tx, tl]. + """ + # Convert anchors to the center coordinate representation. + ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes() + la = tf.sqrt(ha * wa) + ycenter, xcenter, h, w = boxes.get_center_coordinates_and_sizes() + l = tf.sqrt(h * w) + # Avoid NaN in division and log below. + la += EPSILON + l += EPSILON + + tx = (xcenter - xcenter_a) / la + ty = (ycenter - ycenter_a) / la + tl = tf.log(l / la) + # Scales location targets for joint training. + if self._scale_factors: + ty *= self._scale_factors[0] + tx *= self._scale_factors[1] + tl *= self._scale_factors[2] + return tf.transpose(tf.stack([ty, tx, tl])) + + def _decode(self, rel_codes, anchors): + """Decodes relative codes to boxes. + + Args: + rel_codes: a tensor representing N anchor-encoded boxes. + anchors: BoxList of anchors. + + Returns: + boxes: BoxList holding N bounding boxes. + """ + ycenter_a, xcenter_a, ha, wa = anchors.get_center_coordinates_and_sizes() + la = tf.sqrt(ha * wa) + + ty, tx, tl = tf.unstack(tf.transpose(rel_codes)) + if self._scale_factors: + ty /= self._scale_factors[0] + tx /= self._scale_factors[1] + tl /= self._scale_factors[2] + l = tf.exp(tl) * la + ycenter = ty * la + ycenter_a + xcenter = tx * la + xcenter_a + ymin = ycenter - l / 2. + xmin = xcenter - l / 2. + ymax = ycenter + l / 2. + xmax = xcenter + l / 2. + return box_list.BoxList(tf.transpose(tf.stack([ymin, xmin, ymax, xmax]))) diff --git a/workspace/virtuallab/object_detection/box_coders/square_box_coder.pyc b/workspace/virtuallab/object_detection/box_coders/square_box_coder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9ff5bf31daa6bc484743faad0e4cf0224cb8e1e GIT binary patch literal 4434 zcmcIn-EJGl6`mz2N?Oa3e`BXV$)vdm)zXw4BMqvihAYPj0t9ri3IarSuo&$QxhpSs z>7AiS2INhXyB2wXK0$B$B)#o*U!fPhY`=46xsnqDK>&x+9?s03nK^UL_nkRx{^zp) zk8l4LMxy#O@%;dgc>)m&aSZ8+xFIr6oHoR1QyjN=q$#pBF=~tBw(v-!#r<`0ypH~w zINlI(Tj;hp-4p}7UKig8@wE_N9Q(A|QD=iT`Flf%qvsPLFSwD~6qnRd)SpqG9K(E9YG~?aBmu=Y`d56s&*8$Vlf_ zm$0xXVWYCzKr@YP(v>QYWuntzV!M8+%~(e^J=58|Cy&xmnyE6wsxyVf@l5d~bhdhZ zl}AZY)?4`B-{Qg!J!xU3#ez2b{vvmDPJWJkXRR|W)A^9he9^Q+9jS?NC#+_1oKhnd zoJ?`+$|BF^evxbG){uBv=^~P!CFmx~NRt%PT-rj8%i=7JwH)hmP?RGaOv`jcqk07Q z8Hq;8KK)1;@@DX`-=|%1W}rhN3Rnj?C{9k{M1!~!9Aa(J0}QN&r?Jn8t}K)t-5d7g zKtBED+?*IGS`J?-G8>0@*hsEq69iV0!pf3*bnBPNaFbyn$YzVLig!;xK zrfIYrOdWLFB5qO}6a(F;u_1z^PMa2a7vd2ImSS`7j)@dN1@p%^0sN|LTI8}A)PWm?Y1W1ZhI#iVo?&KSYY)1{7z5da+z{<5>HETi)eON~TM8A;`EV87GC zWJ?}|Kdg`M^O*6l%I8e`@CCfOBhy@?vw^)#jf=s3#b>vaj>0g_QyYdo+H4C#Y_+{> z-cI8+?^=U{NP(n>sZYk$(dsiuoK>U|kTYMkk+deI&=5IRvkep=(TK>sSMaRi;qm{X zu|WC3HN?}FST@BHa!oATlyOj*EY`%LEp8wQE!PQx=c*fGxha;uSYBGG`eJd(scwnI zIz{t+nzZFw7~2$!mbi^qHiVnZyNRd1V@Xpt_xrQLQ2`KN25{Fhj4S8cfQ_1`Vg z!)$(uyomwyR@%s%7}()F3S-?VV1t{pBZf`Nq4+;yz?lsM;XJIIF}|x#+4P{w#X$|2>7q>a!SXtTKB>%x>C3}F{^~8 z?WD}>jHc*DHpoKtRYcjaUrkySBXk>KODsltl?ww~rLZSl$WTF(A!m{eoJmqbWlDJA zQqoGahT4X%s=>`%*I*&CKO@X=%CQ5>G(?SP*D>kQpN>BH^l{%&x)=t;9KPUfsd9t6 zMO%WQu_}7jAy6_Ec#MtaMJgBYym@7U;@+(8k*$eh8>a3nQ#`qv!0k#Mn1(XTDa*{+ zK677l_m#bBLXtm17x9nLG{1llTerRI9-fBp`HgLF8?8<6D*kSGOw?=8p@~e?U&8Po zK~&;ybHypfJ0SlQ%em{m*tGcHG={e(n zh`VgZx%$NfG08bsRF=Mkl|!76J`Xx{M+S7^1=}QcAQFw+;1IAK(zgzlYR;dHL5Db=p k@-iP4h~b}EY{yS=K;Gp8=lJ^sE_cwzwZ;|iN~7KUKTlR%+W-In literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/box_coders/square_box_coder_test.py b/workspace/virtuallab/object_detection/box_coders/square_box_coder_test.py new file mode 100644 index 0000000..e6bdcb2 --- /dev/null +++ b/workspace/virtuallab/object_detection/box_coders/square_box_coder_test.py @@ -0,0 +1,114 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.box_coder.square_box_coder.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.box_coders import square_box_coder +from object_detection.core import box_list +from object_detection.utils import test_case + + +class SquareBoxCoderTest(test_case.TestCase): + + def test_correct_relative_codes_with_default_scale(self): + boxes = np.array([[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]], + np.float32) + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + expected_rel_codes = [[-0.790569, -0.263523, -0.293893], + [-0.068041, -0.272166, -0.89588]] + def graph_fn(boxes, anchors): + scale_factors = None + boxes = box_list.BoxList(boxes) + anchors = box_list.BoxList(anchors) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def test_correct_relative_codes_with_non_default_scale(self): + boxes = np.array([[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]], + np.float32) + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + expected_rel_codes = [[-1.581139, -0.790569, -1.175573], + [-0.136083, -0.816497, -3.583519]] + def graph_fn(boxes, anchors): + scale_factors = [2, 3, 4] + boxes = box_list.BoxList(boxes) + anchors = box_list.BoxList(anchors) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-03, + atol=1e-03) + + def test_correct_relative_codes_with_small_width(self): + boxes = np.array([[10.0, 10.0, 10.0000001, 20.0]], np.float32) + anchors = np.array([[15.0, 12.0, 30.0, 18.0]], np.float32) + expected_rel_codes = [[-1.317616, 0., -20.670586]] + def graph_fn(boxes, anchors): + scale_factors = None + boxes = box_list.BoxList(boxes) + anchors = box_list.BoxList(anchors) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + rel_codes = coder.encode(boxes, anchors) + return rel_codes + rel_codes_out = self.execute(graph_fn, [boxes, anchors]) + self.assertAllClose(rel_codes_out, expected_rel_codes, rtol=1e-04, + atol=1e-04) + + def test_correct_boxes_with_default_scale(self): + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + rel_codes = np.array([[-0.5, -0.416666, -0.405465], + [-0.083333, -0.222222, -0.693147]], np.float32) + expected_boxes = [[14.594306, 7.884875, 20.918861, 14.209432], + [0.155051, 0.102989, 0.522474, 0.470412]] + def graph_fn(rel_codes, anchors): + scale_factors = None + anchors = box_list.BoxList(anchors) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors).get() + return boxes + boxes_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(boxes_out, expected_boxes, rtol=1e-04, + atol=1e-04) + + def test_correct_boxes_with_non_default_scale(self): + anchors = np.array([[15.0, 12.0, 30.0, 18.0], [0.1, 0.0, 0.7, 0.9]], + np.float32) + rel_codes = np.array( + [[-1., -1.25, -1.62186], [-0.166667, -0.666667, -2.772588]], np.float32) + expected_boxes = [[14.594306, 7.884875, 20.918861, 14.209432], + [0.155051, 0.102989, 0.522474, 0.470412]] + def graph_fn(rel_codes, anchors): + scale_factors = [2, 3, 4] + anchors = box_list.BoxList(anchors) + coder = square_box_coder.SquareBoxCoder(scale_factors=scale_factors) + boxes = coder.decode(rel_codes, anchors).get() + return boxes + boxes_out = self.execute(graph_fn, [rel_codes, anchors]) + self.assertAllClose(boxes_out, expected_boxes, rtol=1e-04, + atol=1e-04) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/__init__.py b/workspace/virtuallab/object_detection/builders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/virtuallab/object_detection/builders/__init__.pyc b/workspace/virtuallab/object_detection/builders/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..007470768d56c57f0c927bd6472b662992a60a90 GIT binary patch literal 141 zcmZSn%*(a0Z&rLV0~9a>PD*M~v3`7fW?p7Ve7s&kWeEpRs|`>`X-=vg K$bu3eW&i+Y=O2Lp literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/__init__.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd69ffaf33f17f3e1c2f8c087b3b3be750a72553 GIT binary patch literal 146 zcmXr!<>lJgH!Ge22p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CU-tSL`MIh3S@}u& zx%nxnImP-#sl};@MadcZ`AJ!+$tCeAsU<)vGe1v1sWdYuCAFwnKR!M)FS8^*Uaz3? T7KaT`UujON9mwEfAZ7pn+_fbi literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/anchor_generator_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/anchor_generator_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15557fbe2c3799453219ed559c158dc29841045b GIT binary patch literal 3146 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 X-_?&3f8?vKmrs@Q)nfH8b0_~7YKA~~ literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/calibration_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/calibration_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7207619b17d775fa1d65a04ec8bd25eaab87b65e GIT binary patch literal 7897 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/dataset_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/dataset_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aec2ce2f637f77a552fb12f96122b3a08da10c76 GIT binary patch literal 7931 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/decoder_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/decoder_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca90f0b14632117922008ee69da206bcd19f1b91 GIT binary patch literal 1858 zcmaJC%WmXE(Dryx1D6dM%b$Aa&=X8b-ny?uNVCB^Dp?g>o|Wo8;=L{ z7Xb1lAnJq;b*Y!QsTX>#p=&_b!kVRhp#9LdbUmr3jj&~+7 ze}j|G$kkh9CV5gQhT}BnLIJBqa^yH4JQTNjQ$z3~3 zpL&-N!&6WEPW=nRsnhzUD?S0LVZWOf-r57r*0N0-mo7-YQNgkU`(1z@Kp$XhZFC0! z_W-s5h5$PN_i6KO=c2Z*9Xr={TGr05YInJ3_4>5^)+=Sex4`%YYsakAhwO9-JA8gkcybnOo=>|de<`3B2jUa5Gk<)yG-i)mJcyj>AkE-F`+az?L!hAEl8HJMCzW|IAyDxLxah1 zkDcWtqj(WBK}57zVS9koTh=GaGO(O;$#l;$lpxV5j%hfA>mEa?{&Te`V;Z;8#^QK1c70RIOUp+j#Uq2QU(=pLZ!7?zQ{38mOJB13~3M()AM{J5G`w~re6dRTgR>-uFd-vd?MtVZ5% zj(7;)|2c*-VAKAWU4kP|s6I}b*qf(C?~PpT0uCzUF`n%VhE*DY-<$xi_}-LBiJ z^j1;dHr0!tAT+MQ6vtZ+-_ui4T^GVwL+cRwtHqZxffNp&Tp(qmexrw+mh3I=-7v2)r{sjn* BIw1f6 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/graph_rewriter_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/graph_rewriter_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..850bb2a24315895f41bd19c09a0727879b4b17a6 GIT binary patch literal 1330 zcmZux&5qkP5GE=4C$h6~x0|+yq9vM>FLKgcip3&mfc~I8q(BPv#}|P%DVthJR7lF( zHQGxzKypbRpl{G)U!jk|YfgEEo;sxL><>zV!_jDF_|42GKfHZA`TLJQv)2QJUZX2( z1p4Q&&9@*pnj?-mDRDuHXdVZ>H}9WMK#R&jHJsnV=too^#YiNli9W%||9(a_`3dou z_nxEqh?DQpw7&S*Wxb-T=2UE0*)s2zz5xX2$rQWc z-gim7308Dvn?rbbfnMO8czn-?zmZ@4jnVd>r^2=x?qf9H-A(P9Sz0MBb14kvB4=%B z=}NO^O&81n992`*1e|G-Q2JP}%rTs_bIPz%i=OQlgNx34$fH*Tqk01NTb-T9uP{b7DMq|^0UGZ^=LXxy`&m(| z-cE|13AtKZXeZ2x!6-LjP82Xh9oKuAz8M0>?Y-~n@|=FQkanM?3z3GJwrP~Mf=`JX z$+~H67N+1vVcHoNB|D#D?Tel#oo&Okc`_Zl@s1E7nHzPuGPuRKJD0#ZL-GR%J2GumF%5hN-jt;XAHrVQq&+RQ&!XS+!ULX?E7rSUaJ`W1Lr{&MDjFOd>i6Mv;3^*hy(%>zA#2sf~K<{PUg{^{^qP#CfHe-%3MZ%5x$o@s#s;l2Rx eiU{C_C(zZl6p!vej5z=iLqmpy;0Zn;{rEr1(vThi literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/hyperparams_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/hyperparams_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd039232bfc8deb115780de6d56d97926f5e0e9f GIT binary patch literal 12590 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/matcher_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/matcher_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ffc1085efa17364c24e9744f50f42fe22c9fa11 GIT binary patch literal 1458 zcmZWpPp{)N5VzAL&C9mkEuiJk0r^NJ)QZRLX@z)iwF{TsmG&V(5t5b0Zj+G2mF?{A ztJ(|QBXCB11CD&1TsiR-I5BqJzQT*;i9NsZ%=kBxd_Ek;fByPAeS3z`U+CoVVErXb z^)(2F62vg`DqMSsht0ZI^=d!y?b@$`I!r>0cv$v#NIn8Q0NW^u01sLJElSQ&KU5(KeYbL_@S7^R}oMNgE>OCC@ZrT*J$Nb*i(ROR|unCYfj!#j=&Dfu?^$N@#++ zQ7V^p`i44F(A;xNx|*NEHr5OJic1BDU^ku@t5oVj^TUY@8&B}~F#9kc!&JY4&}f4+ zF1?+n-qSsXclal~L8Z^U9o+p#PyPVAV2hdWqpi2?ZEzW?E0F!I2Qu3BbWomcyp2I( znD<}~Snx}54W6B!5d8eChvf)3TSJt-i*kR9_5JdJIkCgn2$0Ai%r^|C2$H@sv?-Dv zS#tEvQ^grE)aLw%?a7$GT!W@%mSt;s@2 zyJcJzk~8a&kZ1df*+4J3Ze`{-`4cIHydWUeL{1JgrJ$?$MMG+ZpCY47Kgpd0VijOtu+m5YjX@3zfd z<;M+QLi4@iidNz^mtdbZ^k~_A0F-2|uPCf3PqUm#@wywg%IfR3qTm8rS_Tk&{pp=< zB!wd89;#ifgw#;Tq9NZrCFj%c*P|z=L`)~+E-qBjD4jMLmpyQp^xa*%_?E*i{w^=F z{KfIk$+%cHLh{@DDjC9QX}UidLVr9<-#AAyG64J-kc^EmA4wM$it5F=y+iWwE6F*# zxjKAT?%>2vMtcgS>#wfw_P4?QK=J`N{O9Q`7d4-iVm_+{u=VSGqV!FEN0=xbQT&hn%py2>Vc!Wccp&dT7qwKL|gGM3_ofB N!uJLkh8N*M?_Z@4uJix^ literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/model_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/model_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e7af027aabd058506ecd6999cae2502428992a9 GIT binary patch literal 28719 zcmeHwd7K=_b>3XNJ3BjjAJ|=71AsUNBo@E|ASs9zZP^km%aJcxmMuBXr6hLl%gQf_b9dq-j-5DC?#NLbIms{I_o`>6 zduA4gezK!K!e!S~SG}sP^VO^Gy(%2(?M-~;%}-|@{qv#FZ-;#Ujl=&e9{XFdP{<4! zAv0`73SlEGzY!yX-)JFPj2W>o<6`i47#-s8fWOn|6n`iDT}GGqyWo!-aq-9D?>4%{ z-wl7lNQgfHe~-~4{vP;~Mz8pjg}!3HF(Ce4_y>(a@%O<$WDJSFzc5@JF-F8c0RN~l zD*i$E$BZ%Y55d35*d+d8_{WWL@sAWHij&5q_(uzyi(8B>;va*5tFcx5n+n^C+l}qw zABR6>q{Ke~{|;lP_$T4tW$X%vtX=s>)wU?=$v^ ze;fR1BQ5^z@b5SFi$4YbqsC+6-vR#tV@mux;Xh~`68|pv4;$0sf244vc+@y5{@sOR z#m9}u#lNTUMDa=EN%8M3ysdcLI4=Hug|`=|7hV-ao(60|FOd5;uYhH_#ZF4RD9WZS^Q5F-c>XV zL;Oz`-d%jfct!kgE4*5KkMSPyA1`EzSB4_kZkAF=l4KWgpE|A0Arx7+xb^)c&%)~)5J z@didG`!l9ytvTyu>lN!g)>Ui4nzyc4re&cK&gSpnc@xhMTIcg`;rTe8PgobNg0*a| zSUD?iK6iJM@kwho|3lXE`5(4U=6}RGo&S{eLjFhb{1~23o6n%mW^PT1#LVm(j<|T6;zR#JL%`1re$y~_zW2SAs zWWJ2h&ztWue#$hApEloZzJl1FF<&*`gWsPuGv-zN{+yXL7x4Q9bI~;M`}3w{F5&l! z=CZkh-(N6uW*)y^GOw8h{Qjc#S?edQ&s$$Ii+5wjFPV;6GRyeRm(9qfPvfx1JtCm{Ls%+8rWipy8W|ysu zvh3XLKt_FqvTa*_(*9!BSzHMaZCWkc4n9|2v}`+9TJ~l9h_aSZm5iM$<_cMrb8cl8 zvxUXALQ^evE@TU+q-AE1SI)^@w=yMb8O;EXoh_~wq^|us|8UB|lVWYl7*6o?sJ%GylP649rY7K5a{wrvK|b{Dhu zwaki@HSvjr*IcU$(|GGHvS%~;rLT}(z)LZ^nptyl1w=>7t2TZ{?N!t>TgW&nTe6qR zs+iFc31=yD-BLE%4`h3MwOT2!spSHyoLRE6&YH3^sFupIuR)piQ5MS!xdKLwles?K zmiWMi#H*#Y33mAsV7xHvc7@pAN_PElW@&>=pYV4;4&z{z6M*BE&#snUWZ7qRN6q;9X;+YPBeGzI z(+`~C^hOzu%b?aZ2=IxoN5O9}fcCMGtfi&gVh%HjJ+P45sOJK0)UM~+4i10AHWFoqnR#ji~neseHqr%`Dp0I#SwIrHO2Hg8{y0rnq34SV)3>-ey#61I3xz z47f7ghGIt`4-SYnM7x7T=g=av7tgmL41VuO8;Za&X7#-z|>Cn8)LF^HP(Z}8DRm&WJ@^bx{hm3Grd6? ztSz-P?nGna96OZp^Gp3HJ?#!3I=pe*vFSkE?d{Cc&yWr*9P)UQl(QYd;rkIx-;dzP z{RocUkKow-2p+#5!4rOhbeA#N%D~Xb(g^P6);PAZ?#XtMXl5O`+n=(*k82FFMz+mQ zrX%t3de0l%TjN=PdlR4#b%J!an{0ga*de#K@rIn-f!gcg%;6`Q%pY>(Xmf}zoIB`C zrHkYCHYBLShmN{QznJ6>_}`dkdXq805!h&%>(ebEM|6lU?O|i2-PCFmidCZ}^>hoJ zG?X#kl;?D&2;PoTwoLFN8A#mAR-15}Y|a=B=464hJF9EPFoSiI)#tA+iwPb@sGH4Ff88CFh@)pZXu!PqS8$Ar+@(!uQ7;HqF5KNP%Ng4zgG9&(&S_>P2%}2e3+6Et+UJP?j zi2ovZD6XBtWAB0Egw{j(uoHPRR1H@`mGHGLwbO~3;e4zTS`V*>KNL1Y*p5cd-*(hoS_zW9J2pFWV*c{nOlIcg z`MDFP<}b`;&Yie;$xU3z7S^m8rOL|fbu)B*y=7`Yu(|X zBstr$zsc5*!wH3x;dr<+qAtU~2((lF>xau&=_Ei%p-Lzps-i>k;c5rmNVOAgwAuwX zR*l2$sCGM@`L6X)HBm{Zr=57d8=;ByaJ8q@hqoTSRl@6$Dt*anvXV%KRH+hkdePw} zrw<+8Uy11sM-CNdpc0!1ReMWur&s$b;YvrPv(igfq8L~0E?ZHx@~0^2*;E#Q;w%zTCp4;GX$A*BdB3!gA*%DqT}$fjV=;$}O)r_Ho^2R+}cXv8n@F z6Y`WtE?qij0VFATz51Pvin(kKIG7$64IXtob)l5Xm9A$CIkQcH$fQ_>+dD3ou9eC+ zN*t0ky2h|No_cRJ-R1W9hKD;~0s~pe6xRw)ZncoJ6wstx2}}#2ZvSf5%sAy*tmTfd zfvlS#T+9K9%bF%S*zIMgq_}~?yN{N^npMu^m_bAf@67nHL3g*ZRtwohizuRp>;ZNp ztItUIO1S{Y(M^=riV!nyVOY(kqwW9(|MfsVx*Wez-Y(W~oFW z(AWQNloDCg?Zr=qO;WI~TLtemd((RzV-pZV0-^IEP$kc@PP(XSzmdIt4?S{BBL zreFngm^K2*+QwibbSYQhbgB_zMO?_0EP{h8Wi!3ijj{XHOW^0Sxk`161Va#q1~J9Y10eE_?tde;XT{ZjiVW5D8R-l6!50a~Bk^d&_g_4k097QM z!0!Mk=#PYxQ3;DB!!e{q>L`5my!)c+2EMi!S%hKy` zagN~Ws6fGbCt8hBl)yU{-)e``Eq;m;olcMVlkhXXS459Kh#vhCK7jB%(ubekP_V$}&}Up`%(%!R6(E0Yik!j(j&2eC1TI>}0JrLWRo8K?|ahALx~ z;mSlMQlS7sL2N&saXgP!Mk@UJV|Wf!7%~OHW=m!Bry?R?bY=fKn!YArlqdzH>T+aC z1kHv_fwkBR(qp?xy5KsbBPdVQC54n0Pbo$?T2z39VAZ7xtkQgyh1N2?#S+Ch2RY{i zq}%%m8oG@45H3=yYYJ)>%hox8k7n$(ViCFH8O}@ayfmPR6 z#9#~g+Ge9Q_6)vKZ>%YDg4+H?S&QZ6666sxwJ$eiP06N)DkrLQ5SS}@EhL4~^Pei1 zG|QKcDuo%PxefWwY&vfA`4&MeK05L$BHbNiX`W6E3yYlrbI%mYi?T3e3izZu62RQN z-W8VS3PTkBW7+NqTXiA3cx_pg*Gd=$Xcfh*2SXjeCjn6IRZd#%ys_Vmq z+7pM;TXgmV%^E_tH&7P0o3ElKbO&3wn>%Th80Rk&aCWEy%eFfZ)D2=m9xFjN!&QLW zM{KL8q2fY4Y%i9fi$afjdq4@4;^v@C@EQsVMR$<9M1R0ZU=)6iA<1OS~jJuVx zEpxKa70`z*+{&<hf1}&$?sK?3v|ay&#@O(;Y_=>_MURl)`%=D_d=n5|2y`6I}xZ zEV@IN+jckQpbr&6uU0W{*o<4Rtp!`v8-gy>u#|u^Dtn``@3k{}BTyUE>4WMvYr`$9 z`VgHJI$VbIG~imKuF?4*9qw%1E>9io#-ZXB-7Wx^cFJD0&NT0%6QT2dI7VM>-Ok7a zRopncJtFFO>p2A@#f8@xXUFPc9-MpX1EgUes7^R;Z(zWxZbl^ObTKx@rdA*2`yD!) z;22{wzLq-+f>WZ1HwIf;R8^R$N~f18`{s$UIVU{zvl?PxkB{4_tE7FC&d2F|g3b@Y zNpCR{4YsBQpEnpOognQm&HTd%Gq&7IkM3()X;p!?0@Gi;&Wyj0&iB*#FrAOUF-DuF zogPZYaA3yiK=lJm^@DWYqSI)@PxAF+a6A;Vw&x}Tz0Np7GComjz$a=+$1h=3wb?SE z{!R!+5ePyth*3$PD)<$lNdGdNVpFFGS`eb}o4_CGlF><0A|&n!VidFDkxmF$TIAyU zz5aCWnpVC80Z)AzrJA3uV>%r59A+YjorR|oRY?cdYlPS!FoXr)i3q+E1-=t=VnAk& zf+7M@!$W2usu7jxg7~7*m;;Fde-Cdde|kn%()8?QuC zAn_4llM$n#J%V5E-q{nE=4a+IoTB&Icgr3<9oJ&0?0gsSMZIF!^nHfTT{>%Yn%L+g z9vk)dMvH9sHL})8qz;V<3k5su1Y7F_lLQ;Z6AP=)BDTKMB!lCsD-4d`u0)y`9Cv;Z zVRKQixoCjFVJAvP2nH7ggX_dzjC;aP0hi567j}ttNE_ijW~>epcbb56)f=QiOfugq zE}`jUzP|=9W49N^ey(FFTIuq3WWNh>;(-~aAS^8m&S!hqv`K4ePEVrZmQ4?*x$iZO z`TzOdA3eWoKdjnm7rqCXo?vF_NCP?($Pc??6O&(AbO`jN2O3PV!I<0weqMK9q)|AY zDlyM8ow|76^mVn$*w^UDxBWcwDnr|`$9AIT{x&>Pkir@(Z@}>1B&R%t%tNDXIAtUj z0g4AfvNMt-p66k9!aqNUGU2>%>|GzShAssgDKY!3VpxEztXj(q2OI2>}i zfX{`%M?0YIqp}a^9o&QqCBV;C2HhL`cLySqJ2g`3wQW z>rihv!}$@sje?FU#9>J_T2~>CL4^p+u1an@E{ed3@>k)XT>WY#TD6@_Z`*H#azD99k)&enrC0 zPI-d@a~T?&~%M04TFZjE#zpSPPMQiE^7f3CWxvO9mivL}B5&qZ@ouiDN#^K-yi zM}enq0-n0V9J||5%fY#npWi6X9Kcp*Q}&L3`kl?B(2I!=+v8*Wz zZ2*M;+O!ZC*c#VhOTA&DNDsROVvG=tueKV}T}@dK)wK~nbF0RQ494ZueB%3l`F(mKApB+|R9KODX^EgqUotxb_t6nUWfj)n7CcjjtO+ zg5MH1C5$Z~klV*Fr|dvy%8$BIU0siiy^>wEY-5-$0NuPJ%2fcDr6P=^G*~I>PaV=- zSBlJjoh^V3RTe*bXLFOjTXba0L%0jlN8Nv3aHjN*MuR9d`e?)*=;4H~<293IktzgOBy)hkRj1rD}NIP!d zm`v;QC;bFKsdIW)DT#;uaW&w0r$2B5G=9QQ6BrrpBmSrW(&xjW-M~k*R;mQB-;Y(h zTLJ^NrGW#nzDkV*B5JkT6$ss+TB!h0JB`Ec0L6yY3ue>=gR&jmy@8C{PZcd zeKpX**d8cQ>*C}>)2@#=>GcLGrEOWqfmI$gGz(?N;aMQqvzfUfEb|cPjyB>Qs|a2o z=ZRpgH;7><$C9K*dMIKPt(MCLLi6>(O{O7%8WAxb36xO|M+vVEOd}jI@h`|i)ljYZ zM_Br@#={A@YTd(+($}P;pe=>U(fJ&mt8@rzsGp_-F(agYhK@k?V~fH+J$*|3Zvet`}<-Vg;(fVXCVAZ9F>61iR8 zgfOHH-+f0+)oOzKl`+{wT&40NNhH5jLRqTZ&W zRx*O@4;|n?2C4oOzeA;yv0JOOe6pWja)WA7^{1KO&(QfRbhu46#&yzGMW%2aJ&rt) zq>1;qJ++gCI8SKokUkL;o|*{LL?0k2L3+X2%O(=_XIZX4M~5mbg#-T~H<81kPmC_; zDb+~?7z1-SHF>gp^J47)s^Xs57-g^^C4sm>#wGo-tNK+0X?)<<==%$FXiIAhpVIt| zz-mp>RRfF}YKUyLi1c0~>6a6mRcxXYbC@?p3(YEoOZ+TfWxjubzBbcWeTfmAGwK)N z7y&T@D~_Lo1tYPJ`Xif%<}WkLzd?udO#KR-KSAeD(vbnxG)P1#_f^KSZ;W2pS`{(n zJR2uz$tKu%ybUBG+kt~%ls4`H2!)2Bg`-8Y=*{4d5Gsa=6To5}24g0K5DxK-pYX;% z8amO4i2yV8a&dW>rtwF}gn7Z}iD~2;UVez7evY&V@lrvD2Requ@$~@!FDwO3rl9ll zXggzl%rEA%x_W3YY7nWY{5E2eD6OYEgpXRq#+&+ev{@YhisFUODLgg@ zr$Yz`cOqbeA6e&_@bG!8%sXdZzBn^?=G@Hr`4eX|=Vsix!L>JN0_*8^KL|I^p#x4GI(&cM9=(kB6ezW-;*q&)8)S& zxSQ~>==Q&cBREtDD1M}YUBOZdc4gI$+MkIFJzj@~ARjx8QwCMw{DJo_GiG)mzKgLR zWixcBn>V-d7wA{$bm-Lq*n-7@S9O0RREa+odd++(^x@D_*z80;ahy<^i9 zU>S9OHGvZu;e6=hk=J6Ev=Rr0Y$G`ngCH_>$c+P9kH0wcu8SAWoS)BJnm?U6J#z)m?1{@~=WE~n2d&>li||GRX_2?p zKOcUe7O5v}DeN#lWRJj+&PrCp7>by!zV4Fh`^$y?8?E<(?cm}F>pFz$MMpQ(Jd4gY zdZu2;P@{~6Ph4BX$;qcJXsc9Cjy<`iToIP*zK;-*hXVW zXxSPQ6W!!?z=~7 zYq_kXBUojJycND-pHr6@JUO8L9#;PqdDQ1I8BQ0TQ+Vu`;W$`rp;95!g=us?hWCzj zEI>HT6wY_@KwLxh0!0YTk0V&tBDFu5N+-m;S^^HnIwpo!?x2j)!0f7xOy6{rzEAflvL`oomZobTrguP_WC4J~wI{_g_(=RzdG*#T zE|GC}yd)=OaMys2Iz{W!`c6ag_z3dw4P+UbqfPbj#^rw_={M-C>?V;ntJK za|)P^*mNTh{Lr@_g$x2N#T~D(7WBJtBv=Ly$kZuQn!nFDpzc5)F5ND^) z`s{A&qp4{mxI=m*=vsQi;D0iIVLIQY?+@twFE}zF7CpA22LuPG9u-XkVr#G^nz%!r zqhTCr+l>a!eHFsi2igZELl`loC_X;1PscVHC?j{v8}RZ=ji13(L5 z$2XzfZVEc2wuM`|qid@gU=!H4L|7LRnQr!^Z`uhXl^*M_7uf4IDGRPa1iz+(iw`v? zR=D=C)`1323!<99pFE@SUE#_#p3DxHjU76;W5d;V9!j@|n1CH_FZ&4RP631GOxx{ORt`r5c;kR*Yoz5g z(GqYRIoha~zWydo`ydmrM~dflWeSqTa zR{kS6Kq{F!??X(32f|i}mOL>Ctzg#j(g+7T`&=!-tj zmsH84GlGsr6jmP6H^oAbyi2_)tPoX{JB#Ys0V7wE-l zxvokwKm1gv(gOsgm-a&tpL>C#B(8O;Uv)O&&_CYBAXa}U{MrX_GH{&lqyZ&^M3<7Eic`A}n@B-DZ~@^^Z9`*1s7g#pMN6A~@Z&A%Zo}Hb-oa z5Yvws;yT^5AwtZP4l$(dh#5c((VlKoUmP&!r0})_F@w&|{4Q_Zcm!xs6ej^`TT?p) z7|QPkRmX)U@glg;kjBMS480W2 z2k@bY`uCtxe;ba6ySOpgl?Fvh^^ZXCo1AfijfHPN>fxZij*%w!L1T3w==P~|`gFaO zs_j@)EVZ4&o|wk+G)KYmjl-ARL8WlSAJ##j-m4 znxt=m9o$VV9S89RmxH<^wKnm(FE?sc`MHADd`uPeqx>=q;3Svx01~ zFb3RB^)}Fe4zUv4db6Sine}`%GO)rW z(;*R})G&Qql+_3wt`2Hc!dQ~Qmf5HE;P2-5aFcW>J!A~}M!C0kHF}?Yt`%F-4!Ep3 zq;N}h$msXgh4``hPyAj3yV6h>WFmln#t4Rb0~qrLH*Pk-SOfCK*5F`K6U-g=UWe3X zI=J<^88UV^wf39L%wOZ|y@?`(dbPTR>K-23;=wNcD{3byln_RL5-~jTMP*YmG7ugS zYP=&p>7{A@)naS|T&npX7~w|khDZy4V&@MA_CyG@idi!|mlHv_l-(4?q{(#(O89LYcWi+>s?tO1{Z=L6)^bI2Q zaZx+L;>(9-i@X|R5r$zj&?s8CQPxe?UI9kpZDASaQ#=m@krQW6R!~QG$0=x`pmRDu zGw-{RY4-B@Q}bsooNu~QB1Ywc!rkK`g-gdnxN;VwF{}@z+oRzEfe)zh$w&F0Mc#SmgD`G=9}BijUr&_BXby*PcbMrr~|eQ^CSaxgC<`_SV`_ zv6fHpj-KDYoD?7s;wyOs?Bt^(Ai37 z8=dWNjA3n`f?EQK3t^$f{XZ(jm?0g5P;M@C#12Mm;+b(T*nhdI+R50lKr&vDBuS0a zEw$3+n__PMJ1cqLrA|{bJ8T?~QM>qwXCF8#`E;9X9$_{QS|Z=|pl!0+{V*l+?lEnX z%^qg6xus%_K_nJTZnAc-MXm&6y)+Za4Y~fSY4wGz-o;Lb)qWE72D({2$}rC~iCuqv zT0O>~ZcT<;Ce;DHC-gh7om5i{=nwSn^~35QL*=-CU__`xZNho8=Bbt{dc$DD8#miD z)?sFTe50H=p!~O9w#i_c89aTj416O}_kXL1e}tKweBeyHOH$jE=IFyz=SZ6jjxmF0 z9;_tZ?W1jSdHlY`@HGl9)zydeeUtMEX7He`y1`Za>dA-81Y@dAN#6D_RYCu@ROR@? zWYDfl-i{0urFY|r`}d0p2HRSOcQA{m={!T{SvbbAdu8U`O4}x@6U^WwGvIY>bCwNk z=-8o+|7`$+SN>}tuB@zIANKK3xgk3S6Y6B`q&BKvr6!x9yJh}HYpq4o{EubB{?{h*Y`+W+eg z#-T=@X8fhmZKg$uAA4&e!OOMeBd0?;p|;Jnr}DqqTs> z+^DnENSlCc#CrsNP_%W{NTY0|8VO_PSxNDX#7BYFg}cDAU+iDkI%;kba~^k{|jZ<3-15` literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/optimizer_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/optimizer_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..307e2e4f8cadc880ef4aaaee6aa3202c8b88dbd1 GIT binary patch literal 4701 zcmeHL&5s*N74I*%J?)Q~@jA2l*ldae0`uY7WFd>9C~TJ9MF=vBCV>S@sMX^t&)BiM zy;bc=W@KNOJ*|`#AWr-T962IxoH%pfP~yf3#0hcQ6Tes8{^&`fkq|;0u&2t^Rqs{3 z_xjbZel>5_>%nh6{Z;tg>z4HwYwfdv_S-0`kBVDei`(28*+XaOc0IJ+QEBXVecL+O z!jm^Dk1O2`+xpOoYqmw-A6v2WBa7F0iTfYh-GDcEnOD%N@=dwC9ivK4lai*NpxK zFJo@ft3dDf(5qjd*D!hk-!OVEcOF;dpVCbp%$yzT%>Jp3Ikx2A)9qn%=A!)yuV#`n zuTP<_MX8M#ND-xiMeNc0?D!_@CF4ny4h}|Q6Ip$4m&}>*nN4Z?%B+=|KyNG)cTYFvScQnvBwThPU#n}jw_RQ0q8FX$$~YM# zAk(ofi-{VHl2|tvUF+(yf~3}sh)3f>H|^-tYpa*@dBx%c@m(0(!OHdMSV+99&@9<* z>EOeoLGS2;SR{Q}qIoYZ;=9~w5-FtX#YaDyOhnAJllFDVydAx@|KQ>G;px%m2Q>#>-XW~vQG!AccPn9CBkuXOi+;fr2Vu{0ns}TO#l7N z)un#7Rg}+TP}W;e%8({ca!L518>?=5x6-4|TAnZPK`QNMn?i14=+~CKfuGO5xqAd} zc8AHq?w9~g?Is69034Rfact*Bl>GHAj99v%DBE|A7&}$%_H=5k@WqOn@aprh!ZR zRtu2%DQ2i$0Da|QJqN#a(EI~58y9MB8qI0}HvgP%6<~8){tZ1<1ETKB;quo1;qt!$ zmu6e-8SH7>X5+P8gB%9tRv>{v!aYgAfcAx8clzZF1z3&_MJxnxGXWfwjP_`!-IR5j zoDfhpsG=P$x2W1irAvLqXjH+Fh z*QiYhBWX(;|0UR&C10n?EKERR&_pt{UV;d>9$`$qjp}(ssJs9XsBaw+T0jdzgo-1- zfH{_UKt#$#F$9XCkz&`MS+WVDWXe#Z%Fn(itC`@QzN* z+y-#kPUfL;{hWs-gL$7|F-5}`ObZ;@Mr&ynohEcz7_+W|+<>v!U`0(bZ|Iy^b3D&s z;t*^JY|ov!hULA39&heJsswK5?|V4`-=``5hxuR8+5a59+cP^<@8INDX%wen zAy91xY>wE1_Tp$P^bPTJ0zwPc3r=h}*ExAlw+_Hs2XV1q*YaLs$4YOHpA*=?>I2yW zdDmCQQ9R8#cX682>+g*x=}9pbY;kq!^pGUxv)J+v@uZrl4v~RB3Fh|99$G(lKJp%B za0CYFkwtLR>Aa!srx{3@&CHUnE5~?gRB}<%gf0RV&o44dwF(@fM-RsXM$&*2P6lN))FX1|D@~4n#x|*evNf6w$ z=?W3{qh6Xw*`cX-sd|H|n^fIGr7Me9Zw{pH=33%RfZ1LDNhHV9iF^yf-OGhA8H=*v z?&=n9z=3X%-D;Yoc@C;>D-$fnwp+`4Gm~3K$%u!fn?a1}ZI`>Pf}87%+ywCa;)a^j z$=SVLRHXUBgJc?WTtS8E?wlJIA5BHv6X%nKPXAw^sC`scgXlGgwISOz9F;_{z5H+` z%3Y-0TaatzeMHOGB`Z!q)SKp~19SP;(B&}XNiPg_D;2RyBr@blC%db6j&IT>gRbq` zIlh@4vODK56djznBW03FHw$ybsT>?gI?d%3s)+b!Co-L_8P!?smgPRymAl|Y`0vU+ YqCkqsjpx+xb?hy;<-5T*g4;pmU)KO89RL6T literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/post_processing_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/post_processing_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97e2ea3e627eac376243d0b61cdd3bfc5d5e7674 GIT binary patch literal 6705 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 V-og#d9VdtMN`0sP<9efh?f;eXZO8xs literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/__pycache__/preprocessor_builder.cpython-36.pyc b/workspace/virtuallab/object_detection/builders/__pycache__/preprocessor_builder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87281306b926401c8f67d078b3db4f6d019b6a86 GIT binary patch literal 10904 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/anchor_generator_builder.py b/workspace/virtuallab/object_detection/builders/anchor_generator_builder.py new file mode 100644 index 0000000..7880210 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/anchor_generator_builder.py @@ -0,0 +1,116 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""A function to build an object detection anchor generator from config.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from six.moves import zip +from object_detection.anchor_generators import flexible_grid_anchor_generator +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.anchor_generators import multiple_grid_anchor_generator +from object_detection.anchor_generators import multiscale_grid_anchor_generator +from object_detection.protos import anchor_generator_pb2 + + +def build(anchor_generator_config): + """Builds an anchor generator based on the config. + + Args: + anchor_generator_config: An anchor_generator.proto object containing the + config for the desired anchor generator. + + Returns: + Anchor generator based on the config. + + Raises: + ValueError: On empty anchor generator proto. + """ + if not isinstance(anchor_generator_config, + anchor_generator_pb2.AnchorGenerator): + raise ValueError('anchor_generator_config not of type ' + 'anchor_generator_pb2.AnchorGenerator') + if anchor_generator_config.WhichOneof( + 'anchor_generator_oneof') == 'grid_anchor_generator': + grid_anchor_generator_config = anchor_generator_config.grid_anchor_generator + return grid_anchor_generator.GridAnchorGenerator( + scales=[float(scale) for scale in grid_anchor_generator_config.scales], + aspect_ratios=[float(aspect_ratio) + for aspect_ratio + in grid_anchor_generator_config.aspect_ratios], + base_anchor_size=[grid_anchor_generator_config.height, + grid_anchor_generator_config.width], + anchor_stride=[grid_anchor_generator_config.height_stride, + grid_anchor_generator_config.width_stride], + anchor_offset=[grid_anchor_generator_config.height_offset, + grid_anchor_generator_config.width_offset]) + elif anchor_generator_config.WhichOneof( + 'anchor_generator_oneof') == 'ssd_anchor_generator': + ssd_anchor_generator_config = anchor_generator_config.ssd_anchor_generator + anchor_strides = None + if ssd_anchor_generator_config.height_stride: + anchor_strides = list( + zip(ssd_anchor_generator_config.height_stride, + ssd_anchor_generator_config.width_stride)) + anchor_offsets = None + if ssd_anchor_generator_config.height_offset: + anchor_offsets = list( + zip(ssd_anchor_generator_config.height_offset, + ssd_anchor_generator_config.width_offset)) + return multiple_grid_anchor_generator.create_ssd_anchors( + num_layers=ssd_anchor_generator_config.num_layers, + min_scale=ssd_anchor_generator_config.min_scale, + max_scale=ssd_anchor_generator_config.max_scale, + scales=[float(scale) for scale in ssd_anchor_generator_config.scales], + aspect_ratios=ssd_anchor_generator_config.aspect_ratios, + interpolated_scale_aspect_ratio=( + ssd_anchor_generator_config.interpolated_scale_aspect_ratio), + base_anchor_size=[ + ssd_anchor_generator_config.base_anchor_height, + ssd_anchor_generator_config.base_anchor_width + ], + anchor_strides=anchor_strides, + anchor_offsets=anchor_offsets, + reduce_boxes_in_lowest_layer=( + ssd_anchor_generator_config.reduce_boxes_in_lowest_layer)) + elif anchor_generator_config.WhichOneof( + 'anchor_generator_oneof') == 'multiscale_anchor_generator': + cfg = anchor_generator_config.multiscale_anchor_generator + return multiscale_grid_anchor_generator.MultiscaleGridAnchorGenerator( + cfg.min_level, + cfg.max_level, + cfg.anchor_scale, + [float(aspect_ratio) for aspect_ratio in cfg.aspect_ratios], + cfg.scales_per_octave, + cfg.normalize_coordinates + ) + elif anchor_generator_config.WhichOneof( + 'anchor_generator_oneof') == 'flexible_grid_anchor_generator': + cfg = anchor_generator_config.flexible_grid_anchor_generator + base_sizes = [] + aspect_ratios = [] + strides = [] + offsets = [] + for anchor_grid in cfg.anchor_grid: + base_sizes.append(tuple(anchor_grid.base_sizes)) + aspect_ratios.append(tuple(anchor_grid.aspect_ratios)) + strides.append((anchor_grid.height_stride, anchor_grid.width_stride)) + offsets.append((anchor_grid.height_offset, anchor_grid.width_offset)) + return flexible_grid_anchor_generator.FlexibleGridAnchorGenerator( + base_sizes, aspect_ratios, strides, offsets, cfg.normalize_coordinates) + else: + raise ValueError('Empty anchor generator.') diff --git a/workspace/virtuallab/object_detection/builders/anchor_generator_builder.pyc b/workspace/virtuallab/object_detection/builders/anchor_generator_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c24757877b37d2372c431283b7905b5bd32848b3 GIT binary patch literal 3342 zcmb_fPj4GV6o0#po!GIHG;LE#Te>Y2Qji>}$5J5zwB)?hvG|hUOVnAic$JbW4Qte?S-eKcG7VR#vtsdO z!RyqiTYN?E26Y-1uM56PomGoBC~4BLMV%J#Rr(2ztx?pZz3mqK)QKZRn20zRU)9yi*dwrFvJTzGK@@(jJv$PlY+c6Z;wgX_+0m6Nq zC1az4csR;(V;L)8MDby)q0Lg(KpEw6YJ$=QORs?bCLUSjJs`bA9mV^J3i^2*1=f>b z>WP)R3c0tiSq??l43XK!*k{ zow}s&kg0HsoI3QQL#c2FOWZM_pih2qDgT4F461bEkg3w3M$r=WAqgeR^woEpbmEd( zp^5DI-lbPQMb%QGZY3%rQDLntVJNDJf`-6V>bn#z%epDBB@`%yku`zqOgW|M9y>Gj zur40{?l5tKPC&mxCsmp((V$8BnqxM}TrN;1wm51y0T~3#!tew1HOfDo6NX6;*Gu6V z3!5u6xJnT`25XDZ$Go`4eawq@%Cum8aav*rX<^YiMJ-uemo$L#uAnz0XxOe&v`!Nw zdPC?p5qINk+?OP7&*ivp(aAEI_h@jNCd)MN1l|#NSIPk5#bx-%;SKML=m&IKqq9af z#mJvxgzOjsMQ1K1Z!jsST#G zAoc;as3#S2CaWa_83ALxWW0LmzA*P_@G;wg(g{j}VjvR4P^CTY=*z4g9gPa_ZSx7x z_i-M+;IX24uACbq`=M5m$J6IPmE)w*@Vwo;uOH&qE4IwedtisoLvOcKH{-QOd4@4o zOfLwUFizvN&)P+qSJ?1+7-4KOQaaAz!J=brcEVR?oTr5!yNeU>zg+jjSgT^ww_!3? zkMlgsA9~MIPYp-r_?#_Iyl%sw{u(JeH&txFUYZ#%>v`sQq|PS=lc#Mxd|HlZvvrZO zELB<0uYzT2Tpylu+VH>%^$5d0z<7@{Z8kvR2+Mh^<2TBRNge`iFpSSovR+RsgX0RV z`uv>8MV1`QduaIjC{ks8(eoth zj5aqRh5C-WD%j7Cln!7q$zCI5>xa3)Zt%@uC;npc{Uz}AHGsz#)~@|*`x7uFINWWi zHqfAazU|~LT&xKvVb&C~?ZrXdJ$ODV%Vmf>MRFIm`n(kv0pEvNX#7=1xr7HQ?jIQ8 zypAJtAXT!&vPw;e39lq@y$g||Y%>u{@cAY;9Q`x2j4YB^8=r4ypUq;#6LoWi`UJCj zYo8m(zr=tAe2zv$=bNc;nWg5*;-b%O>vP8aW!`%W;G1cIE=bI@lM;2Pl4&PdjKeBw zY8#Dh+NKXiDi5-*2@jRwQI%%-FigCz#ur23&`+@1Imm`;XOQjh46{fjddE)Q zVES&{kryS7`_8#23r>4SPSaU;YtFh;$LFq7ac?_Jv)>kenN!2J zg74JY-x$fKoZ_9nQEk}S;5Cl=y4FV}~ zfafZ%1r-E7JL>a$&*wh?r1Nxq)E;JsXdF(gxeKtEllEdNY?H}MmXl@so?GMGR=Fv( kj2HhA1ZMS+)&E%(9$}AA)^VaflS9KjQ*$?vr8A$(zwF*b#Q*>R literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/anchor_generator_builder_test.py b/workspace/virtuallab/object_detection/builders/anchor_generator_builder_test.py new file mode 100644 index 0000000..35cdfca --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/anchor_generator_builder_test.py @@ -0,0 +1,339 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for anchor_generator_builder.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math + +from six.moves import range +from six.moves import zip +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format +from object_detection.anchor_generators import flexible_grid_anchor_generator +from object_detection.anchor_generators import grid_anchor_generator +from object_detection.anchor_generators import multiple_grid_anchor_generator +from object_detection.anchor_generators import multiscale_grid_anchor_generator +from object_detection.builders import anchor_generator_builder +from object_detection.protos import anchor_generator_pb2 + + +class AnchorGeneratorBuilderTest(tf.test.TestCase): + + def assert_almost_list_equal(self, expected_list, actual_list, delta=None): + self.assertEqual(len(expected_list), len(actual_list)) + for expected_item, actual_item in zip(expected_list, actual_list): + self.assertAlmostEqual(expected_item, actual_item, delta=delta) + + def test_build_grid_anchor_generator_with_defaults(self): + anchor_generator_text_proto = """ + grid_anchor_generator { + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + grid_anchor_generator.GridAnchorGenerator) + self.assertListEqual(anchor_generator_object._scales, []) + self.assertListEqual(anchor_generator_object._aspect_ratios, []) + self.assertAllEqual(anchor_generator_object._anchor_offset, [0, 0]) + self.assertAllEqual(anchor_generator_object._anchor_stride, [16, 16]) + self.assertAllEqual(anchor_generator_object._base_anchor_size, [256, 256]) + + def test_build_grid_anchor_generator_with_non_default_parameters(self): + anchor_generator_text_proto = """ + grid_anchor_generator { + height: 128 + width: 512 + height_stride: 10 + width_stride: 20 + height_offset: 30 + width_offset: 40 + scales: [0.4, 2.2] + aspect_ratios: [0.3, 4.5] + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + grid_anchor_generator.GridAnchorGenerator) + self.assert_almost_list_equal(anchor_generator_object._scales, + [0.4, 2.2]) + self.assert_almost_list_equal(anchor_generator_object._aspect_ratios, + [0.3, 4.5]) + self.assertAllEqual(anchor_generator_object._anchor_offset, [30, 40]) + self.assertAllEqual(anchor_generator_object._anchor_stride, [10, 20]) + self.assertAllEqual(anchor_generator_object._base_anchor_size, [128, 512]) + + def test_build_ssd_anchor_generator_with_defaults(self): + anchor_generator_text_proto = """ + ssd_anchor_generator { + aspect_ratios: [1.0] + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiple_grid_anchor_generator. + MultipleGridAnchorGenerator) + for actual_scales, expected_scales in zip( + list(anchor_generator_object._scales), + [(0.1, 0.2, 0.2), + (0.35, 0.418), + (0.499, 0.570), + (0.649, 0.721), + (0.799, 0.871), + (0.949, 0.974)]): + self.assert_almost_list_equal(expected_scales, actual_scales, delta=1e-2) + for actual_aspect_ratio, expected_aspect_ratio in zip( + list(anchor_generator_object._aspect_ratios), + [(1.0, 2.0, 0.5)] + 5 * [(1.0, 1.0)]): + self.assert_almost_list_equal(expected_aspect_ratio, actual_aspect_ratio) + self.assertAllClose(anchor_generator_object._base_anchor_size, [1.0, 1.0]) + + def test_build_ssd_anchor_generator_with_custom_scales(self): + anchor_generator_text_proto = """ + ssd_anchor_generator { + aspect_ratios: [1.0] + scales: [0.1, 0.15, 0.2, 0.4, 0.6, 0.8] + reduce_boxes_in_lowest_layer: false + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiple_grid_anchor_generator. + MultipleGridAnchorGenerator) + for actual_scales, expected_scales in zip( + list(anchor_generator_object._scales), + [(0.1, math.sqrt(0.1 * 0.15)), + (0.15, math.sqrt(0.15 * 0.2)), + (0.2, math.sqrt(0.2 * 0.4)), + (0.4, math.sqrt(0.4 * 0.6)), + (0.6, math.sqrt(0.6 * 0.8)), + (0.8, math.sqrt(0.8 * 1.0))]): + self.assert_almost_list_equal(expected_scales, actual_scales, delta=1e-2) + + def test_build_ssd_anchor_generator_with_custom_interpolated_scale(self): + anchor_generator_text_proto = """ + ssd_anchor_generator { + aspect_ratios: [0.5] + interpolated_scale_aspect_ratio: 0.5 + reduce_boxes_in_lowest_layer: false + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiple_grid_anchor_generator. + MultipleGridAnchorGenerator) + for actual_aspect_ratio, expected_aspect_ratio in zip( + list(anchor_generator_object._aspect_ratios), + 6 * [(0.5, 0.5)]): + self.assert_almost_list_equal(expected_aspect_ratio, actual_aspect_ratio) + + def test_build_ssd_anchor_generator_without_reduced_boxes(self): + anchor_generator_text_proto = """ + ssd_anchor_generator { + aspect_ratios: [1.0] + reduce_boxes_in_lowest_layer: false + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiple_grid_anchor_generator. + MultipleGridAnchorGenerator) + + for actual_scales, expected_scales in zip( + list(anchor_generator_object._scales), + [(0.2, 0.264), + (0.35, 0.418), + (0.499, 0.570), + (0.649, 0.721), + (0.799, 0.871), + (0.949, 0.974)]): + self.assert_almost_list_equal(expected_scales, actual_scales, delta=1e-2) + + for actual_aspect_ratio, expected_aspect_ratio in zip( + list(anchor_generator_object._aspect_ratios), + 6 * [(1.0, 1.0)]): + self.assert_almost_list_equal(expected_aspect_ratio, actual_aspect_ratio) + + self.assertAllClose(anchor_generator_object._base_anchor_size, [1.0, 1.0]) + + def test_build_ssd_anchor_generator_with_non_default_parameters(self): + anchor_generator_text_proto = """ + ssd_anchor_generator { + num_layers: 2 + min_scale: 0.3 + max_scale: 0.8 + aspect_ratios: [2.0] + height_stride: 16 + height_stride: 32 + width_stride: 20 + width_stride: 30 + height_offset: 8 + height_offset: 16 + width_offset: 0 + width_offset: 10 + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiple_grid_anchor_generator. + MultipleGridAnchorGenerator) + + for actual_scales, expected_scales in zip( + list(anchor_generator_object._scales), + [(0.1, 0.3, 0.3), (0.8, 0.894)]): + self.assert_almost_list_equal(expected_scales, actual_scales, delta=1e-2) + + for actual_aspect_ratio, expected_aspect_ratio in zip( + list(anchor_generator_object._aspect_ratios), + [(1.0, 2.0, 0.5), (2.0, 1.0)]): + self.assert_almost_list_equal(expected_aspect_ratio, actual_aspect_ratio) + + for actual_strides, expected_strides in zip( + list(anchor_generator_object._anchor_strides), [(16, 20), (32, 30)]): + self.assert_almost_list_equal(expected_strides, actual_strides) + + for actual_offsets, expected_offsets in zip( + list(anchor_generator_object._anchor_offsets), [(8, 0), (16, 10)]): + self.assert_almost_list_equal(expected_offsets, actual_offsets) + + self.assertAllClose(anchor_generator_object._base_anchor_size, [1.0, 1.0]) + + def test_raise_value_error_on_empty_anchor_genertor(self): + anchor_generator_text_proto = """ + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + with self.assertRaises(ValueError): + anchor_generator_builder.build(anchor_generator_proto) + + def test_build_multiscale_anchor_generator_custom_aspect_ratios(self): + anchor_generator_text_proto = """ + multiscale_anchor_generator { + aspect_ratios: [1.0] + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiscale_grid_anchor_generator. + MultiscaleGridAnchorGenerator) + for level, anchor_grid_info in zip( + range(3, 8), anchor_generator_object._anchor_grid_info): + self.assertEqual(set(anchor_grid_info.keys()), set(['level', 'info'])) + self.assertTrue(level, anchor_grid_info['level']) + self.assertEqual(len(anchor_grid_info['info']), 4) + self.assertAllClose(anchor_grid_info['info'][0], [2**0, 2**0.5]) + self.assertTrue(anchor_grid_info['info'][1], 1.0) + self.assertAllClose(anchor_grid_info['info'][2], + [4.0 * 2**level, 4.0 * 2**level]) + self.assertAllClose(anchor_grid_info['info'][3], [2**level, 2**level]) + self.assertTrue(anchor_generator_object._normalize_coordinates) + + def test_build_multiscale_anchor_generator_with_anchors_in_pixel_coordinates( + self): + anchor_generator_text_proto = """ + multiscale_anchor_generator { + aspect_ratios: [1.0] + normalize_coordinates: false + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + multiscale_grid_anchor_generator. + MultiscaleGridAnchorGenerator) + self.assertFalse(anchor_generator_object._normalize_coordinates) + + def test_build_flexible_anchor_generator(self): + anchor_generator_text_proto = """ + flexible_grid_anchor_generator { + anchor_grid { + base_sizes: [1.5] + aspect_ratios: [1.0] + height_stride: 16 + width_stride: 20 + height_offset: 8 + width_offset: 9 + } + anchor_grid { + base_sizes: [1.0, 2.0] + aspect_ratios: [1.0, 0.5] + height_stride: 32 + width_stride: 30 + height_offset: 10 + width_offset: 11 + } + } + """ + anchor_generator_proto = anchor_generator_pb2.AnchorGenerator() + text_format.Merge(anchor_generator_text_proto, anchor_generator_proto) + anchor_generator_object = anchor_generator_builder.build( + anchor_generator_proto) + self.assertIsInstance(anchor_generator_object, + flexible_grid_anchor_generator. + FlexibleGridAnchorGenerator) + + for actual_base_sizes, expected_base_sizes in zip( + list(anchor_generator_object._base_sizes), [(1.5,), (1.0, 2.0)]): + self.assert_almost_list_equal(expected_base_sizes, actual_base_sizes) + + for actual_aspect_ratios, expected_aspect_ratios in zip( + list(anchor_generator_object._aspect_ratios), [(1.0,), (1.0, 0.5)]): + self.assert_almost_list_equal(expected_aspect_ratios, + actual_aspect_ratios) + + for actual_strides, expected_strides in zip( + list(anchor_generator_object._anchor_strides), [(16, 20), (32, 30)]): + self.assert_almost_list_equal(expected_strides, actual_strides) + + for actual_offsets, expected_offsets in zip( + list(anchor_generator_object._anchor_offsets), [(8, 9), (10, 11)]): + self.assert_almost_list_equal(expected_offsets, actual_offsets) + + self.assertTrue(anchor_generator_object._normalize_coordinates) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/box_coder_builder.py b/workspace/virtuallab/object_detection/builders/box_coder_builder.py new file mode 100644 index 0000000..cc13d5a --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/box_coder_builder.py @@ -0,0 +1,66 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""A function to build an object detection box coder from configuration.""" +from object_detection.box_coders import faster_rcnn_box_coder +from object_detection.box_coders import keypoint_box_coder +from object_detection.box_coders import mean_stddev_box_coder +from object_detection.box_coders import square_box_coder +from object_detection.protos import box_coder_pb2 + + +def build(box_coder_config): + """Builds a box coder object based on the box coder config. + + Args: + box_coder_config: A box_coder.proto object containing the config for the + desired box coder. + + Returns: + BoxCoder based on the config. + + Raises: + ValueError: On empty box coder proto. + """ + if not isinstance(box_coder_config, box_coder_pb2.BoxCoder): + raise ValueError('box_coder_config not of type box_coder_pb2.BoxCoder.') + + if box_coder_config.WhichOneof('box_coder_oneof') == 'faster_rcnn_box_coder': + return faster_rcnn_box_coder.FasterRcnnBoxCoder(scale_factors=[ + box_coder_config.faster_rcnn_box_coder.y_scale, + box_coder_config.faster_rcnn_box_coder.x_scale, + box_coder_config.faster_rcnn_box_coder.height_scale, + box_coder_config.faster_rcnn_box_coder.width_scale + ]) + if box_coder_config.WhichOneof('box_coder_oneof') == 'keypoint_box_coder': + return keypoint_box_coder.KeypointBoxCoder( + box_coder_config.keypoint_box_coder.num_keypoints, + scale_factors=[ + box_coder_config.keypoint_box_coder.y_scale, + box_coder_config.keypoint_box_coder.x_scale, + box_coder_config.keypoint_box_coder.height_scale, + box_coder_config.keypoint_box_coder.width_scale + ]) + if (box_coder_config.WhichOneof('box_coder_oneof') == + 'mean_stddev_box_coder'): + return mean_stddev_box_coder.MeanStddevBoxCoder( + stddev=box_coder_config.mean_stddev_box_coder.stddev) + if box_coder_config.WhichOneof('box_coder_oneof') == 'square_box_coder': + return square_box_coder.SquareBoxCoder(scale_factors=[ + box_coder_config.square_box_coder.y_scale, + box_coder_config.square_box_coder.x_scale, + box_coder_config.square_box_coder.length_scale + ]) + raise ValueError('Empty box coder.') diff --git a/workspace/virtuallab/object_detection/builders/box_coder_builder.pyc b/workspace/virtuallab/object_detection/builders/box_coder_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..216efa86dac7eb849781136281f263fb5452d48b GIT binary patch literal 1781 zcmb_c&5qMZ5H35v6DCZUU9?DCniF$!B!q-CLcoBA3tEso5=gmNj@?P@NxDOK2PP6H zhVwqazQx}6eRu#K0II6(WF~MVk=tMWbX9fv>uP-K_x}6m-z0 z@(|_$EJ7HC5RlwpxdEewmzyj%Vbt_;i{%!KT3&9$ybX&Ej5;WH;0p%sLf(b5qdiJ? zipzs>HfHmmhY`12o(BtJKI{OCK$p zJjrtWAfr;~>EQDMANvR;2k;5k0Ll<9L$LS2HK@&(0elWXv9)Yc*HMGLJ`cf^;95|& zA#c*&5^<*n-5TuGpa;_?TtaY0uA4wD4)i~`8&K}ED}sI<5wVYmz(+*jBO>q-gPn+N zj`$VaO(+ilKP=@#Joq_zmtF4%?iQ4Xbxy>74+5VP!5=Vf@P2B_Ue%Pmhg`Wq4eVnB zpA$Q*NUxBg;>kF*G8e?5nY?24EQxwOAx_NH9^+e(*)sxHBGIup+3KRD(a6IpEIPQf zP=%UuVDB#`+K|Y>1X9!%29s|H%IRY1Rz_7BpX-YkOxlU<0k34xYQM}!}gdg zzMB=Sl zPZ14gdLf6U9uF7Dleryw{w167H{`)6jU8?yRjO#Y#u0%j=yKdaU?1QD{lnlOydCUS z{{iB5upfGP5bm?SAJiU$@HNk5?rM_cI!h9F7vtp>UeKF0Q~=JE-TYWf=OU{L=XKLEIGyk`Ia literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/box_coder_builder_test.py b/workspace/virtuallab/object_detection/builders/box_coder_builder_test.py new file mode 100644 index 0000000..5db9947 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/box_coder_builder_test.py @@ -0,0 +1,136 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for box_coder_builder.""" + +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format +from object_detection.box_coders import faster_rcnn_box_coder +from object_detection.box_coders import keypoint_box_coder +from object_detection.box_coders import mean_stddev_box_coder +from object_detection.box_coders import square_box_coder +from object_detection.builders import box_coder_builder +from object_detection.protos import box_coder_pb2 + + +class BoxCoderBuilderTest(tf.test.TestCase): + + def test_build_faster_rcnn_box_coder_with_defaults(self): + box_coder_text_proto = """ + faster_rcnn_box_coder { + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertIsInstance(box_coder_object, + faster_rcnn_box_coder.FasterRcnnBoxCoder) + self.assertEqual(box_coder_object._scale_factors, [10.0, 10.0, 5.0, 5.0]) + + def test_build_faster_rcnn_box_coder_with_non_default_parameters(self): + box_coder_text_proto = """ + faster_rcnn_box_coder { + y_scale: 6.0 + x_scale: 3.0 + height_scale: 7.0 + width_scale: 8.0 + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertIsInstance(box_coder_object, + faster_rcnn_box_coder.FasterRcnnBoxCoder) + self.assertEqual(box_coder_object._scale_factors, [6.0, 3.0, 7.0, 8.0]) + + def test_build_keypoint_box_coder_with_defaults(self): + box_coder_text_proto = """ + keypoint_box_coder { + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertIsInstance(box_coder_object, keypoint_box_coder.KeypointBoxCoder) + self.assertEqual(box_coder_object._scale_factors, [10.0, 10.0, 5.0, 5.0]) + + def test_build_keypoint_box_coder_with_non_default_parameters(self): + box_coder_text_proto = """ + keypoint_box_coder { + num_keypoints: 6 + y_scale: 6.0 + x_scale: 3.0 + height_scale: 7.0 + width_scale: 8.0 + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertIsInstance(box_coder_object, keypoint_box_coder.KeypointBoxCoder) + self.assertEqual(box_coder_object._num_keypoints, 6) + self.assertEqual(box_coder_object._scale_factors, [6.0, 3.0, 7.0, 8.0]) + + def test_build_mean_stddev_box_coder(self): + box_coder_text_proto = """ + mean_stddev_box_coder { + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertTrue( + isinstance(box_coder_object, + mean_stddev_box_coder.MeanStddevBoxCoder)) + + def test_build_square_box_coder_with_defaults(self): + box_coder_text_proto = """ + square_box_coder { + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertTrue( + isinstance(box_coder_object, square_box_coder.SquareBoxCoder)) + self.assertEqual(box_coder_object._scale_factors, [10.0, 10.0, 5.0]) + + def test_build_square_box_coder_with_non_default_parameters(self): + box_coder_text_proto = """ + square_box_coder { + y_scale: 6.0 + x_scale: 3.0 + length_scale: 7.0 + } + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + box_coder_object = box_coder_builder.build(box_coder_proto) + self.assertTrue( + isinstance(box_coder_object, square_box_coder.SquareBoxCoder)) + self.assertEqual(box_coder_object._scale_factors, [6.0, 3.0, 7.0]) + + def test_raise_error_on_empty_box_coder(self): + box_coder_text_proto = """ + """ + box_coder_proto = box_coder_pb2.BoxCoder() + text_format.Merge(box_coder_text_proto, box_coder_proto) + with self.assertRaises(ValueError): + box_coder_builder.build(box_coder_proto) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/box_predictor_builder.py b/workspace/virtuallab/object_detection/builders/box_predictor_builder.py new file mode 100644 index 0000000..029649d --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/box_predictor_builder.py @@ -0,0 +1,975 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Function to build box predictor from configuration.""" + +import collections +import tensorflow.compat.v1 as tf +from object_detection.predictors import convolutional_box_predictor +from object_detection.predictors import convolutional_keras_box_predictor +from object_detection.predictors import mask_rcnn_box_predictor +from object_detection.predictors import mask_rcnn_keras_box_predictor +from object_detection.predictors import rfcn_box_predictor +from object_detection.predictors import rfcn_keras_box_predictor +from object_detection.predictors.heads import box_head +from object_detection.predictors.heads import class_head +from object_detection.predictors.heads import keras_box_head +from object_detection.predictors.heads import keras_class_head +from object_detection.predictors.heads import keras_mask_head +from object_detection.predictors.heads import mask_head +from object_detection.protos import box_predictor_pb2 + + +def build_convolutional_box_predictor(is_training, + num_classes, + conv_hyperparams_fn, + min_depth, + max_depth, + num_layers_before_predictor, + use_dropout, + dropout_keep_prob, + kernel_size, + box_code_size, + apply_sigmoid_to_scores=False, + add_background_class=True, + class_prediction_bias_init=0.0, + use_depthwise=False, + box_encodings_clip_range=None): + """Builds the ConvolutionalBoxPredictor from the arguments. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + conv_hyperparams_fn: A function to generate tf-slim arg_scope with + hyperparameters for convolution ops. + min_depth: Minimum feature depth prior to predicting box encodings + and class predictions. + max_depth: Maximum feature depth prior to predicting box encodings + and class predictions. If max_depth is set to 0, no additional + feature map will be inserted before location and class predictions. + num_layers_before_predictor: Number of the additional conv layers before + the predictor. + use_dropout: Option to use dropout or not. Note that a single dropout + op is applied here prior to both box and class predictions, which stands + in contrast to the ConvolutionalBoxPredictor below. + dropout_keep_prob: Keep probability for dropout. + This is only used if use_dropout is True. + kernel_size: Size of final convolution kernel. If the + spatial resolution of the feature map is smaller than the kernel size, + then the kernel size is automatically set to be + min(feature_width, feature_height). + box_code_size: Size of encoding for each box. + apply_sigmoid_to_scores: If True, apply the sigmoid on the output + class_predictions. + add_background_class: Whether to add an implicit background class. + class_prediction_bias_init: Constant value to initialize bias of the last + conv2d layer before class prediction. + use_depthwise: Whether to use depthwise convolutions for prediction + steps. Default is False. + box_encodings_clip_range: Min and max values for clipping the box_encodings. + + Returns: + A ConvolutionalBoxPredictor class. + """ + box_prediction_head = box_head.ConvolutionalBoxHead( + is_training=is_training, + box_code_size=box_code_size, + kernel_size=kernel_size, + use_depthwise=use_depthwise, + box_encodings_clip_range=box_encodings_clip_range) + class_prediction_head = class_head.ConvolutionalClassHead( + is_training=is_training, + num_class_slots=num_classes + 1 if add_background_class else num_classes, + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob, + kernel_size=kernel_size, + apply_sigmoid_to_scores=apply_sigmoid_to_scores, + class_prediction_bias_init=class_prediction_bias_init, + use_depthwise=use_depthwise) + other_heads = {} + return convolutional_box_predictor.ConvolutionalBoxPredictor( + is_training=is_training, + num_classes=num_classes, + box_prediction_head=box_prediction_head, + class_prediction_head=class_prediction_head, + other_heads=other_heads, + conv_hyperparams_fn=conv_hyperparams_fn, + num_layers_before_predictor=num_layers_before_predictor, + min_depth=min_depth, + max_depth=max_depth) + + +def build_convolutional_keras_box_predictor(is_training, + num_classes, + conv_hyperparams, + freeze_batchnorm, + inplace_batchnorm_update, + num_predictions_per_location_list, + min_depth, + max_depth, + num_layers_before_predictor, + use_dropout, + dropout_keep_prob, + kernel_size, + box_code_size, + add_background_class=True, + class_prediction_bias_init=0.0, + use_depthwise=False, + box_encodings_clip_range=None, + name='BoxPredictor'): + """Builds the Keras ConvolutionalBoxPredictor from the arguments. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + conv_hyperparams: A `hyperparams_builder.KerasLayerHyperparams` object + containing hyperparameters for convolution ops. + freeze_batchnorm: Whether to freeze batch norm parameters during + training or not. When training with a small batch size (e.g. 1), it is + desirable to freeze batch norm update and use pretrained batch norm + params. + inplace_batchnorm_update: Whether to update batch norm moving average + values inplace. When this is false train op must add a control + dependency on tf.graphkeys.UPDATE_OPS collection in order to update + batch norm statistics. + num_predictions_per_location_list: A list of integers representing the + number of box predictions to be made per spatial location for each + feature map. + min_depth: Minimum feature depth prior to predicting box encodings + and class predictions. + max_depth: Maximum feature depth prior to predicting box encodings + and class predictions. If max_depth is set to 0, no additional + feature map will be inserted before location and class predictions. + num_layers_before_predictor: Number of the additional conv layers before + the predictor. + use_dropout: Option to use dropout or not. Note that a single dropout + op is applied here prior to both box and class predictions, which stands + in contrast to the ConvolutionalBoxPredictor below. + dropout_keep_prob: Keep probability for dropout. + This is only used if use_dropout is True. + kernel_size: Size of final convolution kernel. If the + spatial resolution of the feature map is smaller than the kernel size, + then the kernel size is automatically set to be + min(feature_width, feature_height). + box_code_size: Size of encoding for each box. + add_background_class: Whether to add an implicit background class. + class_prediction_bias_init: constant value to initialize bias of the last + conv2d layer before class prediction. + use_depthwise: Whether to use depthwise convolutions for prediction + steps. Default is False. + box_encodings_clip_range: Min and max values for clipping the box_encodings. + name: A string name scope to assign to the box predictor. If `None`, Keras + will auto-generate one from the class name. + + Returns: + A Keras ConvolutionalBoxPredictor class. + """ + box_prediction_heads = [] + class_prediction_heads = [] + other_heads = {} + + for stack_index, num_predictions_per_location in enumerate( + num_predictions_per_location_list): + box_prediction_heads.append( + keras_box_head.ConvolutionalBoxHead( + is_training=is_training, + box_code_size=box_code_size, + kernel_size=kernel_size, + conv_hyperparams=conv_hyperparams, + freeze_batchnorm=freeze_batchnorm, + num_predictions_per_location=num_predictions_per_location, + use_depthwise=use_depthwise, + box_encodings_clip_range=box_encodings_clip_range, + name='ConvolutionalBoxHead_%d' % stack_index)) + class_prediction_heads.append( + keras_class_head.ConvolutionalClassHead( + is_training=is_training, + num_class_slots=( + num_classes + 1 if add_background_class else num_classes), + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob, + kernel_size=kernel_size, + conv_hyperparams=conv_hyperparams, + freeze_batchnorm=freeze_batchnorm, + num_predictions_per_location=num_predictions_per_location, + class_prediction_bias_init=class_prediction_bias_init, + use_depthwise=use_depthwise, + name='ConvolutionalClassHead_%d' % stack_index)) + + return convolutional_keras_box_predictor.ConvolutionalBoxPredictor( + is_training=is_training, + num_classes=num_classes, + box_prediction_heads=box_prediction_heads, + class_prediction_heads=class_prediction_heads, + other_heads=other_heads, + conv_hyperparams=conv_hyperparams, + num_layers_before_predictor=num_layers_before_predictor, + min_depth=min_depth, + max_depth=max_depth, + freeze_batchnorm=freeze_batchnorm, + inplace_batchnorm_update=inplace_batchnorm_update, + name=name) + + +def build_weight_shared_convolutional_box_predictor( + is_training, + num_classes, + conv_hyperparams_fn, + depth, + num_layers_before_predictor, + box_code_size, + kernel_size=3, + add_background_class=True, + class_prediction_bias_init=0.0, + use_dropout=False, + dropout_keep_prob=0.8, + share_prediction_tower=False, + apply_batch_norm=True, + use_depthwise=False, + score_converter_fn=tf.identity, + box_encodings_clip_range=None, + keyword_args=None): + """Builds and returns a WeightSharedConvolutionalBoxPredictor class. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + conv_hyperparams_fn: A function to generate tf-slim arg_scope with + hyperparameters for convolution ops. + depth: depth of conv layers. + num_layers_before_predictor: Number of the additional conv layers before + the predictor. + box_code_size: Size of encoding for each box. + kernel_size: Size of final convolution kernel. + add_background_class: Whether to add an implicit background class. + class_prediction_bias_init: constant value to initialize bias of the last + conv2d layer before class prediction. + use_dropout: Whether to apply dropout to class prediction head. + dropout_keep_prob: Probability of keeping activiations. + share_prediction_tower: Whether to share the multi-layer tower between box + prediction and class prediction heads. + apply_batch_norm: Whether to apply batch normalization to conv layers in + this predictor. + use_depthwise: Whether to use depthwise separable conv2d instead of conv2d. + score_converter_fn: Callable score converter to perform elementwise op on + class scores. + box_encodings_clip_range: Min and max values for clipping the box_encodings. + keyword_args: A dictionary with additional args. + + Returns: + A WeightSharedConvolutionalBoxPredictor class. + """ + box_prediction_head = box_head.WeightSharedConvolutionalBoxHead( + box_code_size=box_code_size, + kernel_size=kernel_size, + use_depthwise=use_depthwise, + box_encodings_clip_range=box_encodings_clip_range) + class_prediction_head = ( + class_head.WeightSharedConvolutionalClassHead( + num_class_slots=( + num_classes + 1 if add_background_class else num_classes), + kernel_size=kernel_size, + class_prediction_bias_init=class_prediction_bias_init, + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob, + use_depthwise=use_depthwise, + score_converter_fn=score_converter_fn)) + other_heads = {} + return convolutional_box_predictor.WeightSharedConvolutionalBoxPredictor( + is_training=is_training, + num_classes=num_classes, + box_prediction_head=box_prediction_head, + class_prediction_head=class_prediction_head, + other_heads=other_heads, + conv_hyperparams_fn=conv_hyperparams_fn, + depth=depth, + num_layers_before_predictor=num_layers_before_predictor, + kernel_size=kernel_size, + apply_batch_norm=apply_batch_norm, + share_prediction_tower=share_prediction_tower, + use_depthwise=use_depthwise) + + +def build_weight_shared_convolutional_keras_box_predictor( + is_training, + num_classes, + conv_hyperparams, + freeze_batchnorm, + inplace_batchnorm_update, + num_predictions_per_location_list, + depth, + num_layers_before_predictor, + box_code_size, + kernel_size=3, + add_background_class=True, + class_prediction_bias_init=0.0, + use_dropout=False, + dropout_keep_prob=0.8, + share_prediction_tower=False, + apply_batch_norm=True, + use_depthwise=False, + score_converter_fn=tf.identity, + box_encodings_clip_range=None, + name='WeightSharedConvolutionalBoxPredictor', + keyword_args=None): + """Builds the Keras WeightSharedConvolutionalBoxPredictor from the arguments. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + conv_hyperparams: A `hyperparams_builder.KerasLayerHyperparams` object + containing hyperparameters for convolution ops. + freeze_batchnorm: Whether to freeze batch norm parameters during + training or not. When training with a small batch size (e.g. 1), it is + desirable to freeze batch norm update and use pretrained batch norm + params. + inplace_batchnorm_update: Whether to update batch norm moving average + values inplace. When this is false train op must add a control + dependency on tf.graphkeys.UPDATE_OPS collection in order to update + batch norm statistics. + num_predictions_per_location_list: A list of integers representing the + number of box predictions to be made per spatial location for each + feature map. + depth: depth of conv layers. + num_layers_before_predictor: Number of the additional conv layers before + the predictor. + box_code_size: Size of encoding for each box. + kernel_size: Size of final convolution kernel. + add_background_class: Whether to add an implicit background class. + class_prediction_bias_init: constant value to initialize bias of the last + conv2d layer before class prediction. + use_dropout: Whether to apply dropout to class prediction head. + dropout_keep_prob: Probability of keeping activiations. + share_prediction_tower: Whether to share the multi-layer tower between box + prediction and class prediction heads. + apply_batch_norm: Whether to apply batch normalization to conv layers in + this predictor. + use_depthwise: Whether to use depthwise separable conv2d instead of conv2d. + score_converter_fn: Callable score converter to perform elementwise op on + class scores. + box_encodings_clip_range: Min and max values for clipping the box_encodings. + name: A string name scope to assign to the box predictor. If `None`, Keras + will auto-generate one from the class name. + keyword_args: A dictionary with additional args. + + Returns: + A Keras WeightSharedConvolutionalBoxPredictor class. + """ + if len(set(num_predictions_per_location_list)) > 1: + raise ValueError('num predictions per location must be same for all' + 'feature maps, found: {}'.format( + num_predictions_per_location_list)) + num_predictions_per_location = num_predictions_per_location_list[0] + + box_prediction_head = keras_box_head.WeightSharedConvolutionalBoxHead( + box_code_size=box_code_size, + kernel_size=kernel_size, + conv_hyperparams=conv_hyperparams, + num_predictions_per_location=num_predictions_per_location, + use_depthwise=use_depthwise, + box_encodings_clip_range=box_encodings_clip_range, + name='WeightSharedConvolutionalBoxHead') + class_prediction_head = keras_class_head.WeightSharedConvolutionalClassHead( + num_class_slots=( + num_classes + 1 if add_background_class else num_classes), + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob, + kernel_size=kernel_size, + conv_hyperparams=conv_hyperparams, + num_predictions_per_location=num_predictions_per_location, + class_prediction_bias_init=class_prediction_bias_init, + use_depthwise=use_depthwise, + score_converter_fn=score_converter_fn, + name='WeightSharedConvolutionalClassHead') + other_heads = {} + + return ( + convolutional_keras_box_predictor.WeightSharedConvolutionalBoxPredictor( + is_training=is_training, + num_classes=num_classes, + box_prediction_head=box_prediction_head, + class_prediction_head=class_prediction_head, + other_heads=other_heads, + conv_hyperparams=conv_hyperparams, + depth=depth, + num_layers_before_predictor=num_layers_before_predictor, + freeze_batchnorm=freeze_batchnorm, + inplace_batchnorm_update=inplace_batchnorm_update, + kernel_size=kernel_size, + apply_batch_norm=apply_batch_norm, + share_prediction_tower=share_prediction_tower, + use_depthwise=use_depthwise, + name=name)) + + + + +def build_mask_rcnn_keras_box_predictor(is_training, + num_classes, + fc_hyperparams, + freeze_batchnorm, + use_dropout, + dropout_keep_prob, + box_code_size, + add_background_class=True, + share_box_across_classes=False, + predict_instance_masks=False, + conv_hyperparams=None, + mask_height=14, + mask_width=14, + mask_prediction_num_conv_layers=2, + mask_prediction_conv_depth=256, + masks_are_class_agnostic=False, + convolve_then_upsample_masks=False): + """Builds and returns a MaskRCNNKerasBoxPredictor class. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + fc_hyperparams: A `hyperparams_builder.KerasLayerHyperparams` object + containing hyperparameters for fully connected dense ops. + freeze_batchnorm: Whether to freeze batch norm parameters during + training or not. When training with a small batch size (e.g. 1), it is + desirable to freeze batch norm update and use pretrained batch norm + params. + use_dropout: Option to use dropout or not. Note that a single dropout + op is applied here prior to both box and class predictions, which stands + in contrast to the ConvolutionalBoxPredictor below. + dropout_keep_prob: Keep probability for dropout. + This is only used if use_dropout is True. + box_code_size: Size of encoding for each box. + add_background_class: Whether to add an implicit background class. + share_box_across_classes: Whether to share boxes across classes rather + than use a different box for each class. + predict_instance_masks: If True, will add a third stage mask prediction + to the returned class. + conv_hyperparams: A `hyperparams_builder.KerasLayerHyperparams` object + containing hyperparameters for convolution ops. + mask_height: Desired output mask height. The default value is 14. + mask_width: Desired output mask width. The default value is 14. + mask_prediction_num_conv_layers: Number of convolution layers applied to + the image_features in mask prediction branch. + mask_prediction_conv_depth: The depth for the first conv2d_transpose op + applied to the image_features in the mask prediction branch. If set + to 0, the depth of the convolution layers will be automatically chosen + based on the number of object classes and the number of channels in the + image features. + masks_are_class_agnostic: Boolean determining if the mask-head is + class-agnostic or not. + convolve_then_upsample_masks: Whether to apply convolutions on mask + features before upsampling using nearest neighbor resizing. Otherwise, + mask features are resized to [`mask_height`, `mask_width`] using + bilinear resizing before applying convolutions. + + Returns: + A MaskRCNNKerasBoxPredictor class. + """ + box_prediction_head = keras_box_head.MaskRCNNBoxHead( + is_training=is_training, + num_classes=num_classes, + fc_hyperparams=fc_hyperparams, + freeze_batchnorm=freeze_batchnorm, + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob, + box_code_size=box_code_size, + share_box_across_classes=share_box_across_classes) + class_prediction_head = keras_class_head.MaskRCNNClassHead( + is_training=is_training, + num_class_slots=num_classes + 1 if add_background_class else num_classes, + fc_hyperparams=fc_hyperparams, + freeze_batchnorm=freeze_batchnorm, + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob) + third_stage_heads = {} + if predict_instance_masks: + third_stage_heads[ + mask_rcnn_box_predictor. + MASK_PREDICTIONS] = keras_mask_head.MaskRCNNMaskHead( + is_training=is_training, + num_classes=num_classes, + conv_hyperparams=conv_hyperparams, + freeze_batchnorm=freeze_batchnorm, + mask_height=mask_height, + mask_width=mask_width, + mask_prediction_num_conv_layers=mask_prediction_num_conv_layers, + mask_prediction_conv_depth=mask_prediction_conv_depth, + masks_are_class_agnostic=masks_are_class_agnostic, + convolve_then_upsample=convolve_then_upsample_masks) + return mask_rcnn_keras_box_predictor.MaskRCNNKerasBoxPredictor( + is_training=is_training, + num_classes=num_classes, + freeze_batchnorm=freeze_batchnorm, + box_prediction_head=box_prediction_head, + class_prediction_head=class_prediction_head, + third_stage_heads=third_stage_heads) + + +def build_mask_rcnn_box_predictor(is_training, + num_classes, + fc_hyperparams_fn, + use_dropout, + dropout_keep_prob, + box_code_size, + add_background_class=True, + share_box_across_classes=False, + predict_instance_masks=False, + conv_hyperparams_fn=None, + mask_height=14, + mask_width=14, + mask_prediction_num_conv_layers=2, + mask_prediction_conv_depth=256, + masks_are_class_agnostic=False, + convolve_then_upsample_masks=False): + """Builds and returns a MaskRCNNBoxPredictor class. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + fc_hyperparams_fn: A function to generate tf-slim arg_scope with + hyperparameters for fully connected ops. + use_dropout: Option to use dropout or not. Note that a single dropout + op is applied here prior to both box and class predictions, which stands + in contrast to the ConvolutionalBoxPredictor below. + dropout_keep_prob: Keep probability for dropout. + This is only used if use_dropout is True. + box_code_size: Size of encoding for each box. + add_background_class: Whether to add an implicit background class. + share_box_across_classes: Whether to share boxes across classes rather + than use a different box for each class. + predict_instance_masks: If True, will add a third stage mask prediction + to the returned class. + conv_hyperparams_fn: A function to generate tf-slim arg_scope with + hyperparameters for convolution ops. + mask_height: Desired output mask height. The default value is 14. + mask_width: Desired output mask width. The default value is 14. + mask_prediction_num_conv_layers: Number of convolution layers applied to + the image_features in mask prediction branch. + mask_prediction_conv_depth: The depth for the first conv2d_transpose op + applied to the image_features in the mask prediction branch. If set + to 0, the depth of the convolution layers will be automatically chosen + based on the number of object classes and the number of channels in the + image features. + masks_are_class_agnostic: Boolean determining if the mask-head is + class-agnostic or not. + convolve_then_upsample_masks: Whether to apply convolutions on mask + features before upsampling using nearest neighbor resizing. Otherwise, + mask features are resized to [`mask_height`, `mask_width`] using + bilinear resizing before applying convolutions. + + Returns: + A MaskRCNNBoxPredictor class. + """ + box_prediction_head = box_head.MaskRCNNBoxHead( + is_training=is_training, + num_classes=num_classes, + fc_hyperparams_fn=fc_hyperparams_fn, + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob, + box_code_size=box_code_size, + share_box_across_classes=share_box_across_classes) + class_prediction_head = class_head.MaskRCNNClassHead( + is_training=is_training, + num_class_slots=num_classes + 1 if add_background_class else num_classes, + fc_hyperparams_fn=fc_hyperparams_fn, + use_dropout=use_dropout, + dropout_keep_prob=dropout_keep_prob) + third_stage_heads = {} + if predict_instance_masks: + third_stage_heads[ + mask_rcnn_box_predictor. + MASK_PREDICTIONS] = mask_head.MaskRCNNMaskHead( + num_classes=num_classes, + conv_hyperparams_fn=conv_hyperparams_fn, + mask_height=mask_height, + mask_width=mask_width, + mask_prediction_num_conv_layers=mask_prediction_num_conv_layers, + mask_prediction_conv_depth=mask_prediction_conv_depth, + masks_are_class_agnostic=masks_are_class_agnostic, + convolve_then_upsample=convolve_then_upsample_masks) + return mask_rcnn_box_predictor.MaskRCNNBoxPredictor( + is_training=is_training, + num_classes=num_classes, + box_prediction_head=box_prediction_head, + class_prediction_head=class_prediction_head, + third_stage_heads=third_stage_heads) + + +def build_score_converter(score_converter_config, is_training): + """Builds score converter based on the config. + + Builds one of [tf.identity, tf.sigmoid] score converters based on the config + and whether the BoxPredictor is for training or inference. + + Args: + score_converter_config: + box_predictor_pb2.WeightSharedConvolutionalBoxPredictor.score_converter. + is_training: Indicates whether the BoxPredictor is in training mode. + + Returns: + Callable score converter op. + + Raises: + ValueError: On unknown score converter. + """ + if score_converter_config == ( + box_predictor_pb2.WeightSharedConvolutionalBoxPredictor.IDENTITY): + return tf.identity + if score_converter_config == ( + box_predictor_pb2.WeightSharedConvolutionalBoxPredictor.SIGMOID): + return tf.identity if is_training else tf.sigmoid + raise ValueError('Unknown score converter.') + + +BoxEncodingsClipRange = collections.namedtuple('BoxEncodingsClipRange', + ['min', 'max']) + + +def build(argscope_fn, box_predictor_config, is_training, num_classes, + add_background_class=True): + """Builds box predictor based on the configuration. + + Builds box predictor based on the configuration. See box_predictor.proto for + configurable options. Also, see box_predictor.py for more details. + + Args: + argscope_fn: A function that takes the following inputs: + * hyperparams_pb2.Hyperparams proto + * a boolean indicating if the model is in training mode. + and returns a tf slim argscope for Conv and FC hyperparameters. + box_predictor_config: box_predictor_pb2.BoxPredictor proto containing + configuration. + is_training: Whether the models is in training mode. + num_classes: Number of classes to predict. + add_background_class: Whether to add an implicit background class. + + Returns: + box_predictor: box_predictor.BoxPredictor object. + + Raises: + ValueError: On unknown box predictor. + """ + if not isinstance(box_predictor_config, box_predictor_pb2.BoxPredictor): + raise ValueError('box_predictor_config not of type ' + 'box_predictor_pb2.BoxPredictor.') + + box_predictor_oneof = box_predictor_config.WhichOneof('box_predictor_oneof') + + if box_predictor_oneof == 'convolutional_box_predictor': + config_box_predictor = box_predictor_config.convolutional_box_predictor + conv_hyperparams_fn = argscope_fn(config_box_predictor.conv_hyperparams, + is_training) + # Optionally apply clipping to box encodings, when box_encodings_clip_range + # is set. + box_encodings_clip_range = None + if config_box_predictor.HasField('box_encodings_clip_range'): + box_encodings_clip_range = BoxEncodingsClipRange( + min=config_box_predictor.box_encodings_clip_range.min, + max=config_box_predictor.box_encodings_clip_range.max) + return build_convolutional_box_predictor( + is_training=is_training, + num_classes=num_classes, + add_background_class=add_background_class, + conv_hyperparams_fn=conv_hyperparams_fn, + use_dropout=config_box_predictor.use_dropout, + dropout_keep_prob=config_box_predictor.dropout_keep_probability, + box_code_size=config_box_predictor.box_code_size, + kernel_size=config_box_predictor.kernel_size, + num_layers_before_predictor=( + config_box_predictor.num_layers_before_predictor), + min_depth=config_box_predictor.min_depth, + max_depth=config_box_predictor.max_depth, + apply_sigmoid_to_scores=config_box_predictor.apply_sigmoid_to_scores, + class_prediction_bias_init=( + config_box_predictor.class_prediction_bias_init), + use_depthwise=config_box_predictor.use_depthwise, + box_encodings_clip_range=box_encodings_clip_range) + + if box_predictor_oneof == 'weight_shared_convolutional_box_predictor': + config_box_predictor = ( + box_predictor_config.weight_shared_convolutional_box_predictor) + conv_hyperparams_fn = argscope_fn(config_box_predictor.conv_hyperparams, + is_training) + apply_batch_norm = config_box_predictor.conv_hyperparams.HasField( + 'batch_norm') + # During training phase, logits are used to compute the loss. Only apply + # sigmoid at inference to make the inference graph TPU friendly. + score_converter_fn = build_score_converter( + config_box_predictor.score_converter, is_training) + # Optionally apply clipping to box encodings, when box_encodings_clip_range + # is set. + box_encodings_clip_range = None + if config_box_predictor.HasField('box_encodings_clip_range'): + box_encodings_clip_range = BoxEncodingsClipRange( + min=config_box_predictor.box_encodings_clip_range.min, + max=config_box_predictor.box_encodings_clip_range.max) + keyword_args = None + + return build_weight_shared_convolutional_box_predictor( + is_training=is_training, + num_classes=num_classes, + add_background_class=add_background_class, + conv_hyperparams_fn=conv_hyperparams_fn, + depth=config_box_predictor.depth, + num_layers_before_predictor=( + config_box_predictor.num_layers_before_predictor), + box_code_size=config_box_predictor.box_code_size, + kernel_size=config_box_predictor.kernel_size, + class_prediction_bias_init=( + config_box_predictor.class_prediction_bias_init), + use_dropout=config_box_predictor.use_dropout, + dropout_keep_prob=config_box_predictor.dropout_keep_probability, + share_prediction_tower=config_box_predictor.share_prediction_tower, + apply_batch_norm=apply_batch_norm, + use_depthwise=config_box_predictor.use_depthwise, + score_converter_fn=score_converter_fn, + box_encodings_clip_range=box_encodings_clip_range, + keyword_args=keyword_args) + + + if box_predictor_oneof == 'mask_rcnn_box_predictor': + config_box_predictor = box_predictor_config.mask_rcnn_box_predictor + fc_hyperparams_fn = argscope_fn(config_box_predictor.fc_hyperparams, + is_training) + conv_hyperparams_fn = None + if config_box_predictor.HasField('conv_hyperparams'): + conv_hyperparams_fn = argscope_fn( + config_box_predictor.conv_hyperparams, is_training) + return build_mask_rcnn_box_predictor( + is_training=is_training, + num_classes=num_classes, + add_background_class=add_background_class, + fc_hyperparams_fn=fc_hyperparams_fn, + use_dropout=config_box_predictor.use_dropout, + dropout_keep_prob=config_box_predictor.dropout_keep_probability, + box_code_size=config_box_predictor.box_code_size, + share_box_across_classes=( + config_box_predictor.share_box_across_classes), + predict_instance_masks=config_box_predictor.predict_instance_masks, + conv_hyperparams_fn=conv_hyperparams_fn, + mask_height=config_box_predictor.mask_height, + mask_width=config_box_predictor.mask_width, + mask_prediction_num_conv_layers=( + config_box_predictor.mask_prediction_num_conv_layers), + mask_prediction_conv_depth=( + config_box_predictor.mask_prediction_conv_depth), + masks_are_class_agnostic=( + config_box_predictor.masks_are_class_agnostic), + convolve_then_upsample_masks=( + config_box_predictor.convolve_then_upsample_masks)) + + if box_predictor_oneof == 'rfcn_box_predictor': + config_box_predictor = box_predictor_config.rfcn_box_predictor + conv_hyperparams_fn = argscope_fn(config_box_predictor.conv_hyperparams, + is_training) + box_predictor_object = rfcn_box_predictor.RfcnBoxPredictor( + is_training=is_training, + num_classes=num_classes, + conv_hyperparams_fn=conv_hyperparams_fn, + crop_size=[config_box_predictor.crop_height, + config_box_predictor.crop_width], + num_spatial_bins=[config_box_predictor.num_spatial_bins_height, + config_box_predictor.num_spatial_bins_width], + depth=config_box_predictor.depth, + box_code_size=config_box_predictor.box_code_size) + return box_predictor_object + raise ValueError('Unknown box predictor: {}'.format(box_predictor_oneof)) + + +def build_keras(hyperparams_fn, freeze_batchnorm, inplace_batchnorm_update, + num_predictions_per_location_list, box_predictor_config, + is_training, num_classes, add_background_class=True): + """Builds a Keras-based box predictor based on the configuration. + + Builds Keras-based box predictor based on the configuration. + See box_predictor.proto for configurable options. Also, see box_predictor.py + for more details. + + Args: + hyperparams_fn: A function that takes a hyperparams_pb2.Hyperparams + proto and returns a `hyperparams_builder.KerasLayerHyperparams` + for Conv or FC hyperparameters. + freeze_batchnorm: Whether to freeze batch norm parameters during + training or not. When training with a small batch size (e.g. 1), it is + desirable to freeze batch norm update and use pretrained batch norm + params. + inplace_batchnorm_update: Whether to update batch norm moving average + values inplace. When this is false train op must add a control + dependency on tf.graphkeys.UPDATE_OPS collection in order to update + batch norm statistics. + num_predictions_per_location_list: A list of integers representing the + number of box predictions to be made per spatial location for each + feature map. + box_predictor_config: box_predictor_pb2.BoxPredictor proto containing + configuration. + is_training: Whether the models is in training mode. + num_classes: Number of classes to predict. + add_background_class: Whether to add an implicit background class. + + Returns: + box_predictor: box_predictor.KerasBoxPredictor object. + + Raises: + ValueError: On unknown box predictor, or one with no Keras box predictor. + """ + if not isinstance(box_predictor_config, box_predictor_pb2.BoxPredictor): + raise ValueError('box_predictor_config not of type ' + 'box_predictor_pb2.BoxPredictor.') + + box_predictor_oneof = box_predictor_config.WhichOneof('box_predictor_oneof') + + if box_predictor_oneof == 'convolutional_box_predictor': + config_box_predictor = box_predictor_config.convolutional_box_predictor + conv_hyperparams = hyperparams_fn( + config_box_predictor.conv_hyperparams) + # Optionally apply clipping to box encodings, when box_encodings_clip_range + # is set. + box_encodings_clip_range = None + if config_box_predictor.HasField('box_encodings_clip_range'): + box_encodings_clip_range = BoxEncodingsClipRange( + min=config_box_predictor.box_encodings_clip_range.min, + max=config_box_predictor.box_encodings_clip_range.max) + + return build_convolutional_keras_box_predictor( + is_training=is_training, + num_classes=num_classes, + add_background_class=add_background_class, + conv_hyperparams=conv_hyperparams, + freeze_batchnorm=freeze_batchnorm, + inplace_batchnorm_update=inplace_batchnorm_update, + num_predictions_per_location_list=num_predictions_per_location_list, + use_dropout=config_box_predictor.use_dropout, + dropout_keep_prob=config_box_predictor.dropout_keep_probability, + box_code_size=config_box_predictor.box_code_size, + kernel_size=config_box_predictor.kernel_size, + num_layers_before_predictor=( + config_box_predictor.num_layers_before_predictor), + min_depth=config_box_predictor.min_depth, + max_depth=config_box_predictor.max_depth, + class_prediction_bias_init=( + config_box_predictor.class_prediction_bias_init), + use_depthwise=config_box_predictor.use_depthwise, + box_encodings_clip_range=box_encodings_clip_range) + + if box_predictor_oneof == 'weight_shared_convolutional_box_predictor': + config_box_predictor = ( + box_predictor_config.weight_shared_convolutional_box_predictor) + conv_hyperparams = hyperparams_fn(config_box_predictor.conv_hyperparams) + apply_batch_norm = config_box_predictor.conv_hyperparams.HasField( + 'batch_norm') + # During training phase, logits are used to compute the loss. Only apply + # sigmoid at inference to make the inference graph TPU friendly. This is + # required because during TPU inference, model.postprocess is not called. + score_converter_fn = build_score_converter( + config_box_predictor.score_converter, is_training) + # Optionally apply clipping to box encodings, when box_encodings_clip_range + # is set. + box_encodings_clip_range = None + if config_box_predictor.HasField('box_encodings_clip_range'): + box_encodings_clip_range = BoxEncodingsClipRange( + min=config_box_predictor.box_encodings_clip_range.min, + max=config_box_predictor.box_encodings_clip_range.max) + keyword_args = None + + return build_weight_shared_convolutional_keras_box_predictor( + is_training=is_training, + num_classes=num_classes, + conv_hyperparams=conv_hyperparams, + freeze_batchnorm=freeze_batchnorm, + inplace_batchnorm_update=inplace_batchnorm_update, + num_predictions_per_location_list=num_predictions_per_location_list, + depth=config_box_predictor.depth, + num_layers_before_predictor=( + config_box_predictor.num_layers_before_predictor), + box_code_size=config_box_predictor.box_code_size, + kernel_size=config_box_predictor.kernel_size, + add_background_class=add_background_class, + class_prediction_bias_init=( + config_box_predictor.class_prediction_bias_init), + use_dropout=config_box_predictor.use_dropout, + dropout_keep_prob=config_box_predictor.dropout_keep_probability, + share_prediction_tower=config_box_predictor.share_prediction_tower, + apply_batch_norm=apply_batch_norm, + use_depthwise=config_box_predictor.use_depthwise, + score_converter_fn=score_converter_fn, + box_encodings_clip_range=box_encodings_clip_range, + keyword_args=keyword_args) + + if box_predictor_oneof == 'mask_rcnn_box_predictor': + config_box_predictor = box_predictor_config.mask_rcnn_box_predictor + fc_hyperparams = hyperparams_fn(config_box_predictor.fc_hyperparams) + conv_hyperparams = None + if config_box_predictor.HasField('conv_hyperparams'): + conv_hyperparams = hyperparams_fn( + config_box_predictor.conv_hyperparams) + return build_mask_rcnn_keras_box_predictor( + is_training=is_training, + num_classes=num_classes, + add_background_class=add_background_class, + fc_hyperparams=fc_hyperparams, + freeze_batchnorm=freeze_batchnorm, + use_dropout=config_box_predictor.use_dropout, + dropout_keep_prob=config_box_predictor.dropout_keep_probability, + box_code_size=config_box_predictor.box_code_size, + share_box_across_classes=( + config_box_predictor.share_box_across_classes), + predict_instance_masks=config_box_predictor.predict_instance_masks, + conv_hyperparams=conv_hyperparams, + mask_height=config_box_predictor.mask_height, + mask_width=config_box_predictor.mask_width, + mask_prediction_num_conv_layers=( + config_box_predictor.mask_prediction_num_conv_layers), + mask_prediction_conv_depth=( + config_box_predictor.mask_prediction_conv_depth), + masks_are_class_agnostic=( + config_box_predictor.masks_are_class_agnostic), + convolve_then_upsample_masks=( + config_box_predictor.convolve_then_upsample_masks)) + + if box_predictor_oneof == 'rfcn_box_predictor': + config_box_predictor = box_predictor_config.rfcn_box_predictor + conv_hyperparams = hyperparams_fn(config_box_predictor.conv_hyperparams) + box_predictor_object = rfcn_keras_box_predictor.RfcnKerasBoxPredictor( + is_training=is_training, + num_classes=num_classes, + conv_hyperparams=conv_hyperparams, + freeze_batchnorm=freeze_batchnorm, + crop_size=[config_box_predictor.crop_height, + config_box_predictor.crop_width], + num_spatial_bins=[config_box_predictor.num_spatial_bins_height, + config_box_predictor.num_spatial_bins_width], + depth=config_box_predictor.depth, + box_code_size=config_box_predictor.box_code_size) + return box_predictor_object + + raise ValueError( + 'Unknown box predictor for Keras: {}'.format(box_predictor_oneof)) diff --git a/workspace/virtuallab/object_detection/builders/box_predictor_builder.pyc b/workspace/virtuallab/object_detection/builders/box_predictor_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e224323aa418b0183b6758ba436af2719f31a70 GIT binary patch literal 28727 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/box_predictor_builder_test.py b/workspace/virtuallab/object_detection/builders/box_predictor_builder_test.py new file mode 100644 index 0000000..88b9843 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/box_predictor_builder_test.py @@ -0,0 +1,667 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for box_predictor_builder.""" + +import unittest +from unittest import mock # pylint: disable=g-importing-member +import tensorflow.compat.v1 as tf +from google.protobuf import text_format +from object_detection.builders import box_predictor_builder +from object_detection.builders import hyperparams_builder +from object_detection.predictors import mask_rcnn_box_predictor +from object_detection.protos import box_predictor_pb2 +from object_detection.protos import hyperparams_pb2 +from object_detection.utils import tf_version + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only Tests.') +class ConvolutionalBoxPredictorBuilderTest(tf.test.TestCase): + + def test_box_predictor_calls_conv_argscope_fn(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + weight: 0.0003 + } + } + initializer { + truncated_normal_initializer { + mean: 0.0 + stddev: 0.3 + } + } + activation: RELU_6 + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.convolutional_box_predictor.conv_hyperparams.CopyFrom( + hyperparams_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10) + (conv_hyperparams_actual, is_training) = box_predictor._conv_hyperparams_fn + self.assertAlmostEqual((hyperparams_proto.regularizer. + l1_regularizer.weight), + (conv_hyperparams_actual.regularizer.l1_regularizer. + weight)) + self.assertAlmostEqual((hyperparams_proto.initializer. + truncated_normal_initializer.stddev), + (conv_hyperparams_actual.initializer. + truncated_normal_initializer.stddev)) + self.assertAlmostEqual((hyperparams_proto.initializer. + truncated_normal_initializer.mean), + (conv_hyperparams_actual.initializer. + truncated_normal_initializer.mean)) + self.assertEqual(hyperparams_proto.activation, + conv_hyperparams_actual.activation) + self.assertFalse(is_training) + + def test_construct_non_default_conv_box_predictor(self): + box_predictor_text_proto = """ + convolutional_box_predictor { + min_depth: 2 + max_depth: 16 + num_layers_before_predictor: 2 + use_dropout: false + dropout_keep_probability: 0.4 + kernel_size: 3 + box_code_size: 3 + apply_sigmoid_to_scores: true + class_prediction_bias_init: 4.0 + use_depthwise: true + } + """ + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + box_predictor_proto.convolutional_box_predictor.conv_hyperparams.CopyFrom( + hyperparams_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10, + add_background_class=False) + class_head = box_predictor._class_prediction_head + self.assertEqual(box_predictor._min_depth, 2) + self.assertEqual(box_predictor._max_depth, 16) + self.assertEqual(box_predictor._num_layers_before_predictor, 2) + self.assertFalse(class_head._use_dropout) + self.assertAlmostEqual(class_head._dropout_keep_prob, 0.4) + self.assertTrue(class_head._apply_sigmoid_to_scores) + self.assertAlmostEqual(class_head._class_prediction_bias_init, 4.0) + self.assertEqual(class_head._num_class_slots, 10) + self.assertEqual(box_predictor.num_classes, 10) + self.assertFalse(box_predictor._is_training) + self.assertTrue(class_head._use_depthwise) + + def test_construct_default_conv_box_predictor(self): + box_predictor_text_proto = """ + convolutional_box_predictor { + conv_hyperparams { + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + } + }""" + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=hyperparams_builder.build, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + class_head = box_predictor._class_prediction_head + self.assertEqual(box_predictor._min_depth, 0) + self.assertEqual(box_predictor._max_depth, 0) + self.assertEqual(box_predictor._num_layers_before_predictor, 0) + self.assertTrue(class_head._use_dropout) + self.assertAlmostEqual(class_head._dropout_keep_prob, 0.8) + self.assertFalse(class_head._apply_sigmoid_to_scores) + self.assertEqual(class_head._num_class_slots, 91) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertFalse(class_head._use_depthwise) + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only Tests.') +class WeightSharedConvolutionalBoxPredictorBuilderTest(tf.test.TestCase): + + def test_box_predictor_calls_conv_argscope_fn(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + weight: 0.0003 + } + } + initializer { + truncated_normal_initializer { + mean: 0.0 + stddev: 0.3 + } + } + activation: RELU_6 + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + (box_predictor_proto.weight_shared_convolutional_box_predictor + .conv_hyperparams.CopyFrom(hyperparams_proto)) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10) + (conv_hyperparams_actual, is_training) = box_predictor._conv_hyperparams_fn + self.assertAlmostEqual((hyperparams_proto.regularizer. + l1_regularizer.weight), + (conv_hyperparams_actual.regularizer.l1_regularizer. + weight)) + self.assertAlmostEqual((hyperparams_proto.initializer. + truncated_normal_initializer.stddev), + (conv_hyperparams_actual.initializer. + truncated_normal_initializer.stddev)) + self.assertAlmostEqual((hyperparams_proto.initializer. + truncated_normal_initializer.mean), + (conv_hyperparams_actual.initializer. + truncated_normal_initializer.mean)) + self.assertEqual(hyperparams_proto.activation, + conv_hyperparams_actual.activation) + self.assertFalse(is_training) + + def test_construct_non_default_conv_box_predictor(self): + box_predictor_text_proto = """ + weight_shared_convolutional_box_predictor { + depth: 2 + num_layers_before_predictor: 2 + kernel_size: 7 + box_code_size: 3 + class_prediction_bias_init: 4.0 + } + """ + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + (box_predictor_proto.weight_shared_convolutional_box_predictor. + conv_hyperparams.CopyFrom(hyperparams_proto)) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10, + add_background_class=False) + class_head = box_predictor._class_prediction_head + self.assertEqual(box_predictor._depth, 2) + self.assertEqual(box_predictor._num_layers_before_predictor, 2) + self.assertAlmostEqual(class_head._class_prediction_bias_init, 4.0) + self.assertEqual(box_predictor.num_classes, 10) + self.assertFalse(box_predictor._is_training) + self.assertEqual(box_predictor._apply_batch_norm, False) + + def test_construct_non_default_depthwise_conv_box_predictor(self): + box_predictor_text_proto = """ + weight_shared_convolutional_box_predictor { + depth: 2 + num_layers_before_predictor: 2 + kernel_size: 7 + box_code_size: 3 + class_prediction_bias_init: 4.0 + use_depthwise: true + } + """ + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + (box_predictor_proto.weight_shared_convolutional_box_predictor. + conv_hyperparams.CopyFrom(hyperparams_proto)) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10, + add_background_class=False) + class_head = box_predictor._class_prediction_head + self.assertEqual(box_predictor._depth, 2) + self.assertEqual(box_predictor._num_layers_before_predictor, 2) + self.assertEqual(box_predictor._apply_batch_norm, False) + self.assertEqual(box_predictor._use_depthwise, True) + self.assertAlmostEqual(class_head._class_prediction_bias_init, 4.0) + self.assertEqual(box_predictor.num_classes, 10) + self.assertFalse(box_predictor._is_training) + + def test_construct_default_conv_box_predictor(self): + box_predictor_text_proto = """ + weight_shared_convolutional_box_predictor { + conv_hyperparams { + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + } + }""" + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=hyperparams_builder.build, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + self.assertEqual(box_predictor._depth, 0) + self.assertEqual(box_predictor._num_layers_before_predictor, 0) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._apply_batch_norm, False) + + def test_construct_default_conv_box_predictor_with_batch_norm(self): + box_predictor_text_proto = """ + weight_shared_convolutional_box_predictor { + conv_hyperparams { + regularizer { + l1_regularizer { + } + } + batch_norm { + train: true + } + initializer { + truncated_normal_initializer { + } + } + } + }""" + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=hyperparams_builder.build, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + self.assertEqual(box_predictor._depth, 0) + self.assertEqual(box_predictor._num_layers_before_predictor, 0) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._apply_batch_norm, True) + + + + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only Tests.') +class MaskRCNNBoxPredictorBuilderTest(tf.test.TestCase): + + def test_box_predictor_builder_calls_fc_argscope_fn(self): + fc_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + weight: 0.0003 + } + } + initializer { + truncated_normal_initializer { + mean: 0.0 + stddev: 0.3 + } + } + activation: RELU_6 + op: FC + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(fc_hyperparams_text_proto, hyperparams_proto) + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams.CopyFrom( + hyperparams_proto) + mock_argscope_fn = mock.Mock(return_value='arg_scope') + box_predictor = box_predictor_builder.build( + argscope_fn=mock_argscope_fn, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10) + mock_argscope_fn.assert_called_with(hyperparams_proto, False) + self.assertEqual(box_predictor._box_prediction_head._fc_hyperparams_fn, + 'arg_scope') + self.assertEqual(box_predictor._class_prediction_head._fc_hyperparams_fn, + 'arg_scope') + + def test_non_default_mask_rcnn_box_predictor(self): + fc_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + activation: RELU_6 + op: FC + """ + box_predictor_text_proto = """ + mask_rcnn_box_predictor { + use_dropout: true + dropout_keep_probability: 0.8 + box_code_size: 3 + share_box_across_classes: true + } + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(fc_hyperparams_text_proto, hyperparams_proto) + def mock_fc_argscope_builder(fc_hyperparams_arg, is_training): + return (fc_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams.CopyFrom( + hyperparams_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_fc_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + box_head = box_predictor._box_prediction_head + class_head = box_predictor._class_prediction_head + self.assertTrue(box_head._use_dropout) + self.assertTrue(class_head._use_dropout) + self.assertAlmostEqual(box_head._dropout_keep_prob, 0.8) + self.assertAlmostEqual(class_head._dropout_keep_prob, 0.8) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_head._box_code_size, 3) + self.assertEqual(box_head._share_box_across_classes, True) + + def test_build_default_mask_rcnn_box_predictor(self): + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams.op = ( + hyperparams_pb2.Hyperparams.FC) + box_predictor = box_predictor_builder.build( + argscope_fn=mock.Mock(return_value='arg_scope'), + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + box_head = box_predictor._box_prediction_head + class_head = box_predictor._class_prediction_head + self.assertFalse(box_head._use_dropout) + self.assertFalse(class_head._use_dropout) + self.assertAlmostEqual(box_head._dropout_keep_prob, 0.5) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_head._box_code_size, 4) + self.assertEqual(len(box_predictor._third_stage_heads.keys()), 0) + + def test_build_box_predictor_with_mask_branch(self): + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams.op = ( + hyperparams_pb2.Hyperparams.FC) + box_predictor_proto.mask_rcnn_box_predictor.conv_hyperparams.op = ( + hyperparams_pb2.Hyperparams.CONV) + box_predictor_proto.mask_rcnn_box_predictor.predict_instance_masks = True + box_predictor_proto.mask_rcnn_box_predictor.mask_prediction_conv_depth = 512 + box_predictor_proto.mask_rcnn_box_predictor.mask_height = 16 + box_predictor_proto.mask_rcnn_box_predictor.mask_width = 16 + mock_argscope_fn = mock.Mock(return_value='arg_scope') + box_predictor = box_predictor_builder.build( + argscope_fn=mock_argscope_fn, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + mock_argscope_fn.assert_has_calls( + [mock.call(box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams, + True), + mock.call(box_predictor_proto.mask_rcnn_box_predictor.conv_hyperparams, + True)], any_order=True) + box_head = box_predictor._box_prediction_head + class_head = box_predictor._class_prediction_head + third_stage_heads = box_predictor._third_stage_heads + self.assertFalse(box_head._use_dropout) + self.assertFalse(class_head._use_dropout) + self.assertAlmostEqual(box_head._dropout_keep_prob, 0.5) + self.assertAlmostEqual(class_head._dropout_keep_prob, 0.5) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_head._box_code_size, 4) + self.assertIn( + mask_rcnn_box_predictor.MASK_PREDICTIONS, third_stage_heads) + self.assertEqual( + third_stage_heads[mask_rcnn_box_predictor.MASK_PREDICTIONS] + ._mask_prediction_conv_depth, 512) + + def test_build_box_predictor_with_convlve_then_upsample_masks(self): + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams.op = ( + hyperparams_pb2.Hyperparams.FC) + box_predictor_proto.mask_rcnn_box_predictor.conv_hyperparams.op = ( + hyperparams_pb2.Hyperparams.CONV) + box_predictor_proto.mask_rcnn_box_predictor.predict_instance_masks = True + box_predictor_proto.mask_rcnn_box_predictor.mask_prediction_conv_depth = 512 + box_predictor_proto.mask_rcnn_box_predictor.mask_height = 24 + box_predictor_proto.mask_rcnn_box_predictor.mask_width = 24 + box_predictor_proto.mask_rcnn_box_predictor.convolve_then_upsample_masks = ( + True) + + mock_argscope_fn = mock.Mock(return_value='arg_scope') + box_predictor = box_predictor_builder.build( + argscope_fn=mock_argscope_fn, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + mock_argscope_fn.assert_has_calls( + [mock.call(box_predictor_proto.mask_rcnn_box_predictor.fc_hyperparams, + True), + mock.call(box_predictor_proto.mask_rcnn_box_predictor.conv_hyperparams, + True)], any_order=True) + box_head = box_predictor._box_prediction_head + class_head = box_predictor._class_prediction_head + third_stage_heads = box_predictor._third_stage_heads + self.assertFalse(box_head._use_dropout) + self.assertFalse(class_head._use_dropout) + self.assertAlmostEqual(box_head._dropout_keep_prob, 0.5) + self.assertAlmostEqual(class_head._dropout_keep_prob, 0.5) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_head._box_code_size, 4) + self.assertIn( + mask_rcnn_box_predictor.MASK_PREDICTIONS, third_stage_heads) + self.assertEqual( + third_stage_heads[mask_rcnn_box_predictor.MASK_PREDICTIONS] + ._mask_prediction_conv_depth, 512) + self.assertTrue(third_stage_heads[mask_rcnn_box_predictor.MASK_PREDICTIONS] + ._convolve_then_upsample) + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only Tests.') +class RfcnBoxPredictorBuilderTest(tf.test.TestCase): + + def test_box_predictor_calls_fc_argscope_fn(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + weight: 0.0003 + } + } + initializer { + truncated_normal_initializer { + mean: 0.0 + stddev: 0.3 + } + } + activation: RELU_6 + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.rfcn_box_predictor.conv_hyperparams.CopyFrom( + hyperparams_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=False, + num_classes=10) + (conv_hyperparams_actual, is_training) = box_predictor._conv_hyperparams_fn + self.assertAlmostEqual((hyperparams_proto.regularizer. + l1_regularizer.weight), + (conv_hyperparams_actual.regularizer.l1_regularizer. + weight)) + self.assertAlmostEqual((hyperparams_proto.initializer. + truncated_normal_initializer.stddev), + (conv_hyperparams_actual.initializer. + truncated_normal_initializer.stddev)) + self.assertAlmostEqual((hyperparams_proto.initializer. + truncated_normal_initializer.mean), + (conv_hyperparams_actual.initializer. + truncated_normal_initializer.mean)) + self.assertEqual(hyperparams_proto.activation, + conv_hyperparams_actual.activation) + self.assertFalse(is_training) + + def test_non_default_rfcn_box_predictor(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + activation: RELU_6 + """ + box_predictor_text_proto = """ + rfcn_box_predictor { + num_spatial_bins_height: 4 + num_spatial_bins_width: 4 + depth: 4 + box_code_size: 3 + crop_height: 16 + crop_width: 16 + } + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + text_format.Merge(box_predictor_text_proto, box_predictor_proto) + box_predictor_proto.rfcn_box_predictor.conv_hyperparams.CopyFrom( + hyperparams_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._box_code_size, 3) + self.assertEqual(box_predictor._num_spatial_bins, [4, 4]) + self.assertEqual(box_predictor._crop_size, [16, 16]) + + def test_default_rfcn_box_predictor(self): + conv_hyperparams_text_proto = """ + regularizer { + l1_regularizer { + } + } + initializer { + truncated_normal_initializer { + } + } + activation: RELU_6 + """ + hyperparams_proto = hyperparams_pb2.Hyperparams() + text_format.Merge(conv_hyperparams_text_proto, hyperparams_proto) + def mock_conv_argscope_builder(conv_hyperparams_arg, is_training): + return (conv_hyperparams_arg, is_training) + + box_predictor_proto = box_predictor_pb2.BoxPredictor() + box_predictor_proto.rfcn_box_predictor.conv_hyperparams.CopyFrom( + hyperparams_proto) + box_predictor = box_predictor_builder.build( + argscope_fn=mock_conv_argscope_builder, + box_predictor_config=box_predictor_proto, + is_training=True, + num_classes=90) + self.assertEqual(box_predictor.num_classes, 90) + self.assertTrue(box_predictor._is_training) + self.assertEqual(box_predictor._box_code_size, 4) + self.assertEqual(box_predictor._num_spatial_bins, [3, 3]) + self.assertEqual(box_predictor._crop_size, [12, 12]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/calibration_builder.py b/workspace/virtuallab/object_detection/builders/calibration_builder.py new file mode 100644 index 0000000..4adc170 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/calibration_builder.py @@ -0,0 +1,250 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tensorflow ops to calibrate class predictions and background class.""" + +import tensorflow.compat.v1 as tf +from object_detection.utils import shape_utils + + +def _find_interval_containing_new_value(x, new_value): + """Find the index of x (ascending-ordered) after which new_value occurs.""" + new_value_shape = shape_utils.combined_static_and_dynamic_shape(new_value)[0] + x_shape = shape_utils.combined_static_and_dynamic_shape(x)[0] + compare = tf.cast(tf.reshape(new_value, shape=(new_value_shape, 1)) >= + tf.reshape(x, shape=(1, x_shape)), + dtype=tf.int32) + diff = compare[:, 1:] - compare[:, :-1] + interval_idx = tf.argmin(diff, axis=1) + return interval_idx + + +def _tf_linear_interp1d(x_to_interpolate, fn_x, fn_y): + """Tensorflow implementation of 1d linear interpolation. + + Args: + x_to_interpolate: tf.float32 Tensor of shape (num_examples,) over which 1d + linear interpolation is performed. + fn_x: Monotonically-increasing, non-repeating tf.float32 Tensor of shape + (length,) used as the domain to approximate a function. + fn_y: tf.float32 Tensor of shape (length,) used as the range to approximate + a function. + + Returns: + tf.float32 Tensor of shape (num_examples,) + """ + x_pad = tf.concat([fn_x[:1] - 1, fn_x, fn_x[-1:] + 1], axis=0) + y_pad = tf.concat([fn_y[:1], fn_y, fn_y[-1:]], axis=0) + interval_idx = _find_interval_containing_new_value(x_pad, x_to_interpolate) + + # Interpolate + alpha = ( + (x_to_interpolate - tf.gather(x_pad, interval_idx)) / + (tf.gather(x_pad, interval_idx + 1) - tf.gather(x_pad, interval_idx))) + interpolation = ((1 - alpha) * tf.gather(y_pad, interval_idx) + + alpha * tf.gather(y_pad, interval_idx + 1)) + + return interpolation + + +def _function_approximation_proto_to_tf_tensors(x_y_pairs_message): + """Extracts (x,y) pairs from a XYPairs message. + + Args: + x_y_pairs_message: calibration_pb2..XYPairs proto + Returns: + tf_x: tf.float32 tensor of shape (number_xy_pairs,) for function domain. + tf_y: tf.float32 tensor of shape (number_xy_pairs,) for function range. + """ + tf_x = tf.convert_to_tensor([x_y_pair.x + for x_y_pair + in x_y_pairs_message.x_y_pair], + dtype=tf.float32) + tf_y = tf.convert_to_tensor([x_y_pair.y + for x_y_pair + in x_y_pairs_message.x_y_pair], + dtype=tf.float32) + return tf_x, tf_y + + +def _get_class_id_function_dict(calibration_config): + """Create a dictionary mapping class id to function approximations. + + Args: + calibration_config: calibration_pb2 proto containing + id_function_approximations. + Returns: + Dictionary mapping a class id to a tuple of TF tensors to be used for + function approximation. + """ + class_id_function_dict = {} + class_id_xy_pairs_map = ( + calibration_config.class_id_function_approximations.class_id_xy_pairs_map) + for class_id in class_id_xy_pairs_map: + class_id_function_dict[class_id] = ( + _function_approximation_proto_to_tf_tensors( + class_id_xy_pairs_map[class_id])) + + return class_id_function_dict + + +def build(calibration_config): + """Returns a function that calibrates Tensorflow model scores. + + All returned functions are expected to apply positive monotonic + transformations to inputs (i.e. score ordering is strictly preserved or + adjacent scores are mapped to the same score, but an input of lower value + should never be exceed an input of higher value after transformation). For + class-agnostic calibration, positive monotonicity should hold across all + scores. In class-specific cases, positive monotonicity should hold within each + class. + + Args: + calibration_config: calibration_pb2.CalibrationConfig proto. + Returns: + Function that that accepts class_predictions_with_background and calibrates + the output based on calibration_config's parameters. + Raises: + ValueError: No calibration builder defined for "Oneof" in + calibration_config. + """ + + # Linear Interpolation (usually used as a result of calibration via + # isotonic regression). + if calibration_config.WhichOneof('calibrator') == 'function_approximation': + + def calibration_fn(class_predictions_with_background): + """Calibrate predictions via 1-d linear interpolation. + + Predictions scores are linearly interpolated based on a class-agnostic + function approximation. Note that the 0-indexed background class is also + transformed. + + Args: + class_predictions_with_background: tf.float32 tensor of shape + [batch_size, num_anchors, num_classes + 1] containing scores on the + interval [0,1]. This is usually produced by a sigmoid or softmax layer + and the result of calling the `predict` method of a detection model. + + Returns: + tf.float32 tensor of the same shape as the input with values on the + interval [0, 1]. + """ + # Flattening Tensors and then reshaping at the end. + flat_class_predictions_with_background = tf.reshape( + class_predictions_with_background, shape=[-1]) + fn_x, fn_y = _function_approximation_proto_to_tf_tensors( + calibration_config.function_approximation.x_y_pairs) + updated_scores = _tf_linear_interp1d( + flat_class_predictions_with_background, fn_x, fn_y) + + # Un-flatten the scores + original_detections_shape = shape_utils.combined_static_and_dynamic_shape( + class_predictions_with_background) + calibrated_class_predictions_with_background = tf.reshape( + updated_scores, + shape=original_detections_shape, + name='calibrate_scores') + return calibrated_class_predictions_with_background + + elif (calibration_config.WhichOneof('calibrator') == + 'class_id_function_approximations'): + + def calibration_fn(class_predictions_with_background): + """Calibrate predictions per class via 1-d linear interpolation. + + Prediction scores are linearly interpolated with class-specific function + approximations. Note that after calibration, an anchor's class scores will + not necessarily sum to 1, and score ordering may change, depending on each + class' calibration parameters. + + Args: + class_predictions_with_background: tf.float32 tensor of shape + [batch_size, num_anchors, num_classes + 1] containing scores on the + interval [0,1]. This is usually produced by a sigmoid or softmax layer + and the result of calling the `predict` method of a detection model. + + Returns: + tf.float32 tensor of the same shape as the input with values on the + interval [0, 1]. + + Raises: + KeyError: Calibration parameters are not present for a class. + """ + class_id_function_dict = _get_class_id_function_dict(calibration_config) + + # Tensors are split by class and then recombined at the end to recover + # the input's original shape. If a class id does not have calibration + # parameters, it is left unchanged. + class_tensors = tf.unstack(class_predictions_with_background, axis=-1) + calibrated_class_tensors = [] + for class_id, class_tensor in enumerate(class_tensors): + flat_class_tensor = tf.reshape(class_tensor, shape=[-1]) + if class_id in class_id_function_dict: + output_tensor = _tf_linear_interp1d( + x_to_interpolate=flat_class_tensor, + fn_x=class_id_function_dict[class_id][0], + fn_y=class_id_function_dict[class_id][1]) + else: + tf.logging.info( + 'Calibration parameters for class id `%d` not not found', + class_id) + output_tensor = flat_class_tensor + calibrated_class_tensors.append(output_tensor) + + combined_calibrated_tensor = tf.stack(calibrated_class_tensors, axis=1) + input_shape = shape_utils.combined_static_and_dynamic_shape( + class_predictions_with_background) + calibrated_class_predictions_with_background = tf.reshape( + combined_calibrated_tensor, + shape=input_shape, + name='calibrate_scores') + return calibrated_class_predictions_with_background + + elif (calibration_config.WhichOneof('calibrator') == + 'temperature_scaling_calibration'): + + def calibration_fn(class_predictions_with_background): + """Calibrate predictions via temperature scaling. + + Predictions logits scores are scaled by the temperature scaler. Note that + the 0-indexed background class is also transformed. + + Args: + class_predictions_with_background: tf.float32 tensor of shape + [batch_size, num_anchors, num_classes + 1] containing logits scores. + This is usually produced before a sigmoid or softmax layer. + + Returns: + tf.float32 tensor of the same shape as the input. + + Raises: + ValueError: If temperature scaler is of incorrect value. + """ + scaler = calibration_config.temperature_scaling_calibration.scaler + if scaler <= 0: + raise ValueError('The scaler in temperature scaling must be positive.') + calibrated_class_predictions_with_background = tf.math.divide( + class_predictions_with_background, + scaler, + name='calibrate_score') + return calibrated_class_predictions_with_background + + # TODO(zbeaver): Add sigmoid calibration. + else: + raise ValueError('No calibration builder defined for "Oneof" in ' + 'calibration_config.') + + return calibration_fn diff --git a/workspace/virtuallab/object_detection/builders/calibration_builder.pyc b/workspace/virtuallab/object_detection/builders/calibration_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ad0a212887396e529adecacbfb1c3374c0b04ef GIT binary patch literal 8702 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 len(filenames): + num_readers = len(filenames) + tf.logging.warning('num_readers has been reduced to %d to match input file ' + 'shards.' % num_readers) + filename_dataset = tf.data.Dataset.from_tensor_slices(filenames) + if config.shuffle: + filename_dataset = filename_dataset.shuffle( + config.filenames_shuffle_buffer_size) + elif num_readers > 1: + tf.logging.warning('`shuffle` is false, but the input data stream is ' + 'still slightly shuffled since `num_readers` > 1.') + if filename_shard_fn: + filename_dataset = filename_shard_fn(filename_dataset) + + filename_dataset = filename_dataset.repeat(config.num_epochs or None) + records_dataset = filename_dataset.apply( + tf.data.experimental.parallel_interleave( + file_read_func, + cycle_length=num_readers, + block_length=config.read_block_length, + sloppy=config.shuffle)) + if config.shuffle: + records_dataset = records_dataset.shuffle(config.shuffle_buffer_size) + return records_dataset + + +def read_dataset(file_read_func, input_files, config, filename_shard_fn=None): + """Reads multiple datasets with sampling. + + Args: + file_read_func: Function to use in tf_data.parallel_interleave, to read + every individual file into a tf.data.Dataset. + input_files: A list of file paths to read. + config: A input_reader_builder.InputReader object. + filename_shard_fn: optional, A function used to shard filenames across + replicas. This function takes as input a TF dataset of filenames and is + expected to return its sharded version. It is useful when the dataset is + being loaded on one of possibly many replicas and we want to evenly shard + the files between the replicas. + + Returns: + A tf.data.Dataset of (undecoded) tf-records based on config. + + Raises: + RuntimeError: If no files are found at the supplied path(s). + """ + if config.sample_from_datasets_weights: + tf.logging.info('Reading weighted datasets: %s' % input_files) + if len(input_files) != len(config.sample_from_datasets_weights): + raise ValueError('Expected the number of input files to be the same as ' + 'the number of dataset sample weights. But got ' + '[input_files, sample_from_datasets_weights]: [' + + input_files + ', ' + + str(config.sample_from_datasets_weights) + ']') + tf.logging.info('Sampling from datasets %s with weights %s' % + (input_files, config.sample_from_datasets_weights)) + records_datasets = [] + for input_file in input_files: + records_dataset = _read_dataset_internal(file_read_func, [input_file], + config, filename_shard_fn) + records_datasets.append(records_dataset) + dataset_weights = list(config.sample_from_datasets_weights) + return tf.data.experimental.sample_from_datasets(records_datasets, + dataset_weights) + else: + tf.logging.info('Reading unweighted datasets: %s' % input_files) + return _read_dataset_internal(file_read_func, input_files, config, + filename_shard_fn) + + +def shard_function_for_context(input_context): + """Returns a function that shards filenames based on the input context.""" + + if input_context is None: + return None + + def shard_fn(dataset): + return dataset.shard( + input_context.num_input_pipelines, input_context.input_pipeline_id) + + return shard_fn + + +def build(input_reader_config, batch_size=None, transform_input_data_fn=None, + input_context=None, reduce_to_frame_fn=None): + """Builds a tf.data.Dataset. + + Builds a tf.data.Dataset by applying the `transform_input_data_fn` on all + records. Applies a padded batch to the resulting dataset. + + Args: + input_reader_config: A input_reader_pb2.InputReader object. + batch_size: Batch size. If batch size is None, no batching is performed. + transform_input_data_fn: Function to apply transformation to all records, + or None if no extra decoding is required. + input_context: optional, A tf.distribute.InputContext object used to + shard filenames and compute per-replica batch_size when this function + is being called per-replica. + reduce_to_frame_fn: Function that extracts frames from tf.SequenceExample + type input data. + + Returns: + A tf.data.Dataset based on the input_reader_config. + + Raises: + ValueError: On invalid input reader proto. + ValueError: If no input paths are specified. + """ + if not isinstance(input_reader_config, input_reader_pb2.InputReader): + raise ValueError('input_reader_config not of type ' + 'input_reader_pb2.InputReader.') + + decoder = decoder_builder.build(input_reader_config) + + if input_reader_config.WhichOneof('input_reader') == 'tf_record_input_reader': + config = input_reader_config.tf_record_input_reader + if not config.input_path: + raise ValueError('At least one input path must be specified in ' + '`input_reader_config`.') + def dataset_map_fn(dataset, fn_to_map, batch_size=None, + input_reader_config=None): + """Handles whether or not to use the legacy map function. + + Args: + dataset: A tf.Dataset. + fn_to_map: The function to be mapped for that dataset. + batch_size: Batch size. If batch size is None, no batching is performed. + input_reader_config: A input_reader_pb2.InputReader object. + + Returns: + A tf.data.Dataset mapped with fn_to_map. + """ + if hasattr(dataset, 'map_with_legacy_function'): + if batch_size: + num_parallel_calls = batch_size * ( + input_reader_config.num_parallel_batches) + else: + num_parallel_calls = input_reader_config.num_parallel_map_calls + dataset = dataset.map_with_legacy_function( + fn_to_map, num_parallel_calls=num_parallel_calls) + else: + dataset = dataset.map(fn_to_map, tf.data.experimental.AUTOTUNE) + return dataset + shard_fn = shard_function_for_context(input_context) + if input_context is not None: + batch_size = input_context.get_per_replica_batch_size(batch_size) + dataset = read_dataset( + functools.partial(tf.data.TFRecordDataset, buffer_size=8 * 1000 * 1000), + config.input_path[:], input_reader_config, filename_shard_fn=shard_fn) + if input_reader_config.sample_1_of_n_examples > 1: + dataset = dataset.shard(input_reader_config.sample_1_of_n_examples, 0) + # TODO(rathodv): make batch size a required argument once the old binaries + # are deleted. + dataset = dataset_map_fn(dataset, decoder.decode, batch_size, + input_reader_config) + if reduce_to_frame_fn: + dataset = reduce_to_frame_fn(dataset, dataset_map_fn, batch_size, + input_reader_config) + if transform_input_data_fn is not None: + dataset = dataset_map_fn(dataset, transform_input_data_fn, + batch_size, input_reader_config) + if batch_size: + dataset = dataset.batch(batch_size, + drop_remainder=input_reader_config.drop_remainder) + dataset = dataset.prefetch(input_reader_config.num_prefetch_batches) + return dataset + + raise ValueError('Unsupported input_reader_config.') diff --git a/workspace/virtuallab/object_detection/builders/dataset_builder.pyc b/workspace/virtuallab/object_detection/builders/dataset_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98d90c3e5132fa66597a2c29f44e79beb42b1000 GIT binary patch literal 9095 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/dataset_builder_test.py b/workspace/virtuallab/object_detection/builders/dataset_builder_test.py new file mode 100644 index 0000000..7f3358a --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/dataset_builder_test.py @@ -0,0 +1,716 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for dataset_builder.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import numpy as np +from six.moves import range +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format + +from object_detection.builders import dataset_builder +from object_detection.core import standard_fields as fields +from object_detection.dataset_tools import seq_example_util +from object_detection.protos import input_reader_pb2 +from object_detection.utils import dataset_util +from object_detection.utils import test_case + +# pylint: disable=g-import-not-at-top +try: + from tensorflow.contrib import lookup as contrib_lookup +except ImportError: + # TF 2.0 doesn't ship with contrib. + pass +# pylint: enable=g-import-not-at-top + + +def get_iterator_next_for_testing(dataset, is_tf2): + iterator = dataset.make_initializable_iterator() + if not is_tf2: + tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, iterator.initializer) + return iterator.get_next() + + +def _get_labelmap_path(): + """Returns an absolute path to label map file.""" + parent_path = os.path.dirname(tf.resource_loader.get_data_files_path()) + return os.path.join(parent_path, 'data', + 'pet_label_map.pbtxt') + + +class DatasetBuilderTest(test_case.TestCase): + + def create_tf_record(self, has_additional_channels=False, num_shards=1, + num_examples_per_shard=1): + + def dummy_jpeg_fn(): + image_tensor = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + additional_channels_tensor = np.random.randint( + 255, size=(4, 5, 1)).astype(np.uint8) + encoded_jpeg = tf.image.encode_jpeg(image_tensor) + encoded_additional_channels_jpeg = tf.image.encode_jpeg( + additional_channels_tensor) + + return encoded_jpeg, encoded_additional_channels_jpeg + + encoded_jpeg, encoded_additional_channels_jpeg = self.execute( + dummy_jpeg_fn, []) + + tmp_dir = self.get_temp_dir() + flat_mask = (4 * 5) * [1.0] + + for i in range(num_shards): + path = os.path.join(tmp_dir, '%05d.tfrecord' % i) + writer = tf.python_io.TFRecordWriter(path) + + for j in range(num_examples_per_shard): + if num_shards > 1: + source_id = (str(i) + '_' + str(j)).encode() + else: + source_id = str(j).encode() + + features = { + 'image/source_id': dataset_util.bytes_feature(source_id), + 'image/encoded': dataset_util.bytes_feature(encoded_jpeg), + 'image/format': dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/height': dataset_util.int64_feature(4), + 'image/width': dataset_util.int64_feature(5), + 'image/object/bbox/xmin': dataset_util.float_list_feature([0.0]), + 'image/object/bbox/xmax': dataset_util.float_list_feature([1.0]), + 'image/object/bbox/ymin': dataset_util.float_list_feature([0.0]), + 'image/object/bbox/ymax': dataset_util.float_list_feature([1.0]), + 'image/object/class/label': dataset_util.int64_list_feature([2]), + 'image/object/mask': dataset_util.float_list_feature(flat_mask), + } + + if has_additional_channels: + additional_channels_key = 'image/additional_channels/encoded' + features[additional_channels_key] = dataset_util.bytes_list_feature( + [encoded_additional_channels_jpeg] * 2) + + example = tf.train.Example(features=tf.train.Features(feature=features)) + writer.write(example.SerializeToString()) + + writer.close() + + return os.path.join(self.get_temp_dir(), '?????.tfrecord') + + def _make_random_serialized_jpeg_images(self, num_frames, image_height, + image_width): + def graph_fn(): + images = tf.cast(tf.random.uniform( + [num_frames, image_height, image_width, 3], + maxval=256, + dtype=tf.int32), dtype=tf.uint8) + images_list = tf.unstack(images, axis=0) + encoded_images_list = [tf.io.encode_jpeg(image) for image in images_list] + return encoded_images_list + + encoded_images = self.execute(graph_fn, []) + return encoded_images + + def create_tf_record_sequence_example(self): + path = os.path.join(self.get_temp_dir(), 'seq_tfrecord') + writer = tf.python_io.TFRecordWriter(path) + + num_frames = 4 + image_height = 4 + image_width = 5 + image_source_ids = [str(i) for i in range(num_frames)] + with self.test_session(): + encoded_images = self._make_random_serialized_jpeg_images( + num_frames, image_height, image_width) + sequence_example_serialized = seq_example_util.make_sequence_example( + dataset_name='video_dataset', + video_id='video', + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + image_source_ids=image_source_ids, + image_format='JPEG', + is_annotated=[[1], [1], [1], [1]], + bboxes=[ + [[]], # Frame 0. + [[0., 0., 1., 1.]], # Frame 1. + [[0., 0., 1., 1.], + [0.1, 0.1, 0.2, 0.2]], # Frame 2. + [[]], # Frame 3. + ], + label_strings=[ + [], # Frame 0. + ['Abyssinian'], # Frame 1. + ['Abyssinian', 'american_bulldog'], # Frame 2. + [], # Frame 3 + ]).SerializeToString() + writer.write(sequence_example_serialized) + writer.close() + return path + + def test_build_tf_record_input_reader(self): + tf_record_path = self.create_tf_record() + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + def graph_fn(): + return get_iterator_next_for_testing( + dataset_builder.build(input_reader_proto, batch_size=1), + self.is_tf2()) + + output_dict = self.execute(graph_fn, []) + + self.assertNotIn( + fields.InputDataFields.groundtruth_instance_masks, output_dict) + self.assertEqual((1, 4, 5, 3), + output_dict[fields.InputDataFields.image].shape) + self.assertAllEqual([[2]], + output_dict[fields.InputDataFields.groundtruth_classes]) + self.assertEqual( + (1, 1, 4), output_dict[fields.InputDataFields.groundtruth_boxes].shape) + self.assertAllEqual( + [0.0, 0.0, 1.0, 1.0], + output_dict[fields.InputDataFields.groundtruth_boxes][0][0]) + + def get_mock_reduce_to_frame_fn(self): + def mock_reduce_to_frame_fn(dataset, dataset_map_fn, batch_size, config): + def get_frame(tensor_dict): + out_tensor_dict = {} + out_tensor_dict[fields.InputDataFields.source_id] = ( + tensor_dict[fields.InputDataFields.source_id][0]) + return out_tensor_dict + return dataset_map_fn(dataset, get_frame, batch_size, config) + return mock_reduce_to_frame_fn + + def test_build_tf_record_input_reader_sequence_example_train(self): + tf_record_path = self.create_tf_record_sequence_example() + label_map_path = _get_labelmap_path() + input_type = 'TF_SEQUENCE_EXAMPLE' + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + input_type: {1} + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path, input_type) + input_reader_proto = input_reader_pb2.InputReader() + input_reader_proto.label_map_path = label_map_path + text_format.Merge(input_reader_text_proto, input_reader_proto) + reduce_to_frame_fn = self.get_mock_reduce_to_frame_fn() + + def graph_fn(): + return get_iterator_next_for_testing( + dataset_builder.build(input_reader_proto, batch_size=1, + reduce_to_frame_fn=reduce_to_frame_fn), + self.is_tf2()) + + output_dict = self.execute(graph_fn, []) + + self.assertEqual((1,), + output_dict[fields.InputDataFields.source_id].shape) + + def test_build_tf_record_input_reader_sequence_example_test(self): + tf_record_path = self.create_tf_record_sequence_example() + input_type = 'TF_SEQUENCE_EXAMPLE' + label_map_path = _get_labelmap_path() + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + input_type: {1} + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path, input_type) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + input_reader_proto.label_map_path = label_map_path + reduce_to_frame_fn = self.get_mock_reduce_to_frame_fn() + def graph_fn(): + return get_iterator_next_for_testing( + dataset_builder.build(input_reader_proto, batch_size=1, + reduce_to_frame_fn=reduce_to_frame_fn), + self.is_tf2()) + + output_dict = self.execute(graph_fn, []) + + self.assertEqual((1,), + output_dict[fields.InputDataFields.source_id].shape) + + def test_build_tf_record_input_reader_and_load_instance_masks(self): + tf_record_path = self.create_tf_record() + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + load_instance_masks: true + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + def graph_fn(): + return get_iterator_next_for_testing( + dataset_builder.build(input_reader_proto, batch_size=1), + self.is_tf2() + ) + + output_dict = self.execute(graph_fn, []) + self.assertAllEqual( + (1, 1, 4, 5), + output_dict[fields.InputDataFields.groundtruth_instance_masks].shape) + + def test_build_tf_record_input_reader_with_batch_size_two(self): + tf_record_path = self.create_tf_record() + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + def one_hot_class_encoding_fn(tensor_dict): + tensor_dict[fields.InputDataFields.groundtruth_classes] = tf.one_hot( + tensor_dict[fields.InputDataFields.groundtruth_classes] - 1, depth=3) + return tensor_dict + + def graph_fn(): + return dataset_builder.make_initializable_iterator( + dataset_builder.build( + input_reader_proto, + transform_input_data_fn=one_hot_class_encoding_fn, + batch_size=2)).get_next() + + output_dict = self.execute(graph_fn, []) + + self.assertAllEqual([2, 4, 5, 3], + output_dict[fields.InputDataFields.image].shape) + self.assertAllEqual( + [2, 1, 3], + output_dict[fields.InputDataFields.groundtruth_classes].shape) + self.assertAllEqual( + [2, 1, 4], output_dict[fields.InputDataFields.groundtruth_boxes].shape) + self.assertAllEqual([[[0.0, 0.0, 1.0, 1.0]], [[0.0, 0.0, 1.0, 1.0]]], + output_dict[fields.InputDataFields.groundtruth_boxes]) + + def test_build_tf_record_input_reader_with_batch_size_two_and_masks(self): + tf_record_path = self.create_tf_record() + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + load_instance_masks: true + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + def one_hot_class_encoding_fn(tensor_dict): + tensor_dict[fields.InputDataFields.groundtruth_classes] = tf.one_hot( + tensor_dict[fields.InputDataFields.groundtruth_classes] - 1, depth=3) + return tensor_dict + + def graph_fn(): + return dataset_builder.make_initializable_iterator( + dataset_builder.build( + input_reader_proto, + transform_input_data_fn=one_hot_class_encoding_fn, + batch_size=2)).get_next() + + output_dict = self.execute(graph_fn, []) + + self.assertAllEqual( + [2, 1, 4, 5], + output_dict[fields.InputDataFields.groundtruth_instance_masks].shape) + + def test_raises_error_with_no_input_paths(self): + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + load_instance_masks: true + """ + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + with self.assertRaises(ValueError): + dataset_builder.build(input_reader_proto, batch_size=1) + + def test_sample_all_data(self): + tf_record_path = self.create_tf_record(num_examples_per_shard=2) + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + sample_1_of_n_examples: 1 + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + def graph_fn(): + dataset = dataset_builder.build(input_reader_proto, batch_size=1) + sample1_ds = dataset.take(1) + sample2_ds = dataset.skip(1) + iter1 = dataset_builder.make_initializable_iterator(sample1_ds) + iter2 = dataset_builder.make_initializable_iterator(sample2_ds) + + return iter1.get_next(), iter2.get_next() + + output_dict1, output_dict2 = self.execute(graph_fn, []) + self.assertAllEqual([b'0'], output_dict1[fields.InputDataFields.source_id]) + self.assertEqual([b'1'], output_dict2[fields.InputDataFields.source_id]) + + def test_sample_one_of_n_shards(self): + tf_record_path = self.create_tf_record(num_examples_per_shard=4) + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + sample_1_of_n_examples: 2 + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + def graph_fn(): + dataset = dataset_builder.build(input_reader_proto, batch_size=1) + sample1_ds = dataset.take(1) + sample2_ds = dataset.skip(1) + iter1 = dataset_builder.make_initializable_iterator(sample1_ds) + iter2 = dataset_builder.make_initializable_iterator(sample2_ds) + + return iter1.get_next(), iter2.get_next() + + output_dict1, output_dict2 = self.execute(graph_fn, []) + self.assertAllEqual([b'0'], output_dict1[fields.InputDataFields.source_id]) + self.assertEqual([b'2'], output_dict2[fields.InputDataFields.source_id]) + + def test_no_input_context(self): + """Test that all samples are read with no input context given.""" + tf_record_path = self.create_tf_record(num_examples_per_shard=16, + num_shards=2) + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + num_epochs: 1 + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + for i in range(4): + + # pylint:disable=cell-var-from-loop + def graph_fn(): + dataset = dataset_builder.build(input_reader_proto, batch_size=8) + dataset = dataset.skip(i) + return get_iterator_next_for_testing(dataset, self.is_tf2()) + + batch = self.execute(graph_fn, []) + self.assertEqual(batch['image'].shape, (8, 4, 5, 3)) + + def graph_fn_last_batch(): + dataset = dataset_builder.build(input_reader_proto, batch_size=8) + dataset = dataset.skip(4) + return get_iterator_next_for_testing(dataset, self.is_tf2()) + + self.assertRaises(tf.errors.OutOfRangeError, self.execute, + compute_fn=graph_fn_last_batch, inputs=[]) + + def test_with_input_context(self): + """Test that a subset is read with input context given.""" + tf_record_path = self.create_tf_record(num_examples_per_shard=16, + num_shards=2) + + input_reader_text_proto = """ + shuffle: false + num_readers: 1 + num_epochs: 1 + tf_record_input_reader {{ + input_path: '{0}' + }} + """.format(tf_record_path) + input_reader_proto = input_reader_pb2.InputReader() + text_format.Merge(input_reader_text_proto, input_reader_proto) + + input_context = tf.distribute.InputContext( + num_input_pipelines=2, input_pipeline_id=0, num_replicas_in_sync=4 + ) + + for i in range(8): + + # pylint:disable=cell-var-from-loop + def graph_fn(): + + dataset = dataset_builder.build(input_reader_proto, batch_size=8, + input_context=input_context) + dataset = dataset.skip(i) + return get_iterator_next_for_testing(dataset, self.is_tf2()) + + batch = self.execute(graph_fn, []) + self.assertEqual(batch['image'].shape, (2, 4, 5, 3)) + + def graph_fn_last_batch(): + dataset = dataset_builder.build(input_reader_proto, batch_size=8, + input_context=input_context) + dataset = dataset.skip(8) + return get_iterator_next_for_testing(dataset, self.is_tf2()) + + self.assertRaises(tf.errors.OutOfRangeError, self.execute, + compute_fn=graph_fn_last_batch, inputs=[]) + + +class ReadDatasetTest(test_case.TestCase): + + def setUp(self): + self._path_template = os.path.join(self.get_temp_dir(), 'examples_%s.txt') + + for i in range(5): + path = self._path_template % i + with tf.gfile.Open(path, 'wb') as f: + f.write('\n'.join([str(i + 1), str((i + 1) * 10)])) + + self._shuffle_path_template = os.path.join(self.get_temp_dir(), + 'shuffle_%s.txt') + for i in range(2): + path = self._shuffle_path_template % i + with tf.gfile.Open(path, 'wb') as f: + f.write('\n'.join([str(i)] * 5)) + + super(ReadDatasetTest, self).setUp() + + def _get_dataset_next(self, files, config, batch_size, num_batches_skip=0): + + def decode_func(value): + return [tf.string_to_number(value, out_type=tf.int32)] + + dataset = dataset_builder.read_dataset(tf.data.TextLineDataset, files, + config) + dataset = dataset.map(decode_func) + dataset = dataset.batch(batch_size) + + if num_batches_skip > 0: + dataset = dataset.skip(num_batches_skip) + + return get_iterator_next_for_testing(dataset, self.is_tf2()) + + def _assert_item_count(self, data, item, percentage): + self.assertAlmostEqual(data.count(item)/len(data), percentage, places=1) + + def test_make_initializable_iterator_with_hashTable(self): + + def graph_fn(): + keys = [1, 0, -1] + dataset = tf.data.Dataset.from_tensor_slices([[1, 2, -1, 5]]) + try: + # Dynamically try to load the tf v2 lookup, falling back to contrib + lookup = tf.compat.v2.lookup + hash_table_class = tf.compat.v2.lookup.StaticHashTable + except AttributeError: + lookup = contrib_lookup + hash_table_class = contrib_lookup.HashTable + table = hash_table_class( + initializer=lookup.KeyValueTensorInitializer( + keys=keys, values=list(reversed(keys))), + default_value=100) + dataset = dataset.map(table.lookup) + return dataset_builder.make_initializable_iterator(dataset).get_next() + + result = self.execute(graph_fn, []) + self.assertAllEqual(result, [-1, 100, 1, 100]) + + def test_read_dataset_sample_from_datasets_weights_equal_weight(self): + """Ensure that the files' values are equally-weighted.""" + config = input_reader_pb2.InputReader() + config.num_readers = 2 + config.shuffle = False + config.sample_from_datasets_weights.extend([0.5, 0.5]) + + def graph_fn(): + return self._get_dataset_next( + [self._path_template % '0', self._path_template % '1'], + config, + batch_size=1000) + + data = list(self.execute(graph_fn, [])) + self.assertEqual(len(data), 1000) + self._assert_item_count(data, 1, 0.25) + self._assert_item_count(data, 10, 0.25) + self._assert_item_count(data, 2, 0.25) + self._assert_item_count(data, 20, 0.25) + + def test_read_dataset_sample_from_datasets_weights_zero_weight(self): + """Ensure that the files' values are equally-weighted.""" + config = input_reader_pb2.InputReader() + config.num_readers = 2 + config.shuffle = False + config.sample_from_datasets_weights.extend([1.0, 0.0]) + + def graph_fn(): + return self._get_dataset_next( + [self._path_template % '0', self._path_template % '1'], + config, + batch_size=1000) + + data = list(self.execute(graph_fn, [])) + self.assertEqual(len(data), 1000) + self._assert_item_count(data, 1, 0.5) + self._assert_item_count(data, 10, 0.5) + self._assert_item_count(data, 2, 0.0) + self._assert_item_count(data, 20, 0.0) + + def test_read_dataset_sample_from_datasets_weights_unbalanced(self): + """Ensure that the files' values are equally-weighted.""" + config = input_reader_pb2.InputReader() + config.num_readers = 2 + config.shuffle = False + config.sample_from_datasets_weights.extend([0.1, 0.9]) + + def graph_fn(): + return self._get_dataset_next( + [self._path_template % '0', self._path_template % '1'], + config, + batch_size=1000) + + data = list(self.execute(graph_fn, [])) + self.assertEqual(len(data), 1000) + self._assert_item_count(data, 1, 0.05) + self._assert_item_count(data, 10, 0.05) + self._assert_item_count(data, 2, 0.45) + self._assert_item_count(data, 20, 0.45) + + def test_read_dataset(self): + config = input_reader_pb2.InputReader() + config.num_readers = 1 + config.shuffle = False + + def graph_fn(): + return self._get_dataset_next( + [self._path_template % '*'], config, batch_size=20) + + data = self.execute(graph_fn, []) + # Note that the execute function extracts single outputs if the return + # value is of size 1. + self.assertCountEqual( + data, [ + 1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 1, 10, 2, 20, 3, 30, 4, 40, 5, + 50 + ]) + + def test_reduce_num_reader(self): + config = input_reader_pb2.InputReader() + config.num_readers = 10 + config.shuffle = False + + def graph_fn(): + return self._get_dataset_next( + [self._path_template % '*'], config, batch_size=20) + + data = self.execute(graph_fn, []) + # Note that the execute function extracts single outputs if the return + # value is of size 1. + self.assertCountEqual( + data, [ + 1, 10, 2, 20, 3, 30, 4, 40, 5, 50, 1, 10, 2, 20, 3, 30, 4, 40, 5, + 50 + ]) + + def test_enable_shuffle(self): + config = input_reader_pb2.InputReader() + config.num_readers = 1 + config.shuffle = True + + tf.set_random_seed(1) # Set graph level seed. + + def graph_fn(): + return self._get_dataset_next( + [self._shuffle_path_template % '*'], config, batch_size=10) + expected_non_shuffle_output = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + data = self.execute(graph_fn, []) + + self.assertTrue( + np.any(np.not_equal(data, expected_non_shuffle_output))) + + def test_disable_shuffle_(self): + config = input_reader_pb2.InputReader() + config.num_readers = 1 + config.shuffle = False + + def graph_fn(): + return self._get_dataset_next( + [self._shuffle_path_template % '*'], config, batch_size=10) + expected_non_shuffle_output1 = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + expected_non_shuffle_output2 = [1, 1, 1, 1, 1, 0, 0, 0, 0, 0] + + # Note that the execute function extracts single outputs if the return + # value is of size 1. + data = self.execute(graph_fn, []) + self.assertTrue(all(data == expected_non_shuffle_output1) or + all(data == expected_non_shuffle_output2)) + + def test_read_dataset_single_epoch(self): + config = input_reader_pb2.InputReader() + config.num_epochs = 1 + config.num_readers = 1 + config.shuffle = False + + def graph_fn(): + return self._get_dataset_next( + [self._path_template % '0'], config, batch_size=30) + + data = self.execute(graph_fn, []) + + # Note that the execute function extracts single outputs if the return + # value is of size 1. + self.assertAllEqual(data, [1, 10]) + + # First batch will retrieve as much as it can, second batch will fail. + def graph_fn_second_batch(): + return self._get_dataset_next( + [self._path_template % '0'], config, batch_size=30, + num_batches_skip=1) + + self.assertRaises(tf.errors.OutOfRangeError, self.execute, + compute_fn=graph_fn_second_batch, inputs=[]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/decoder_builder.py b/workspace/virtuallab/object_detection/builders/decoder_builder.py new file mode 100644 index 0000000..3cf92a8 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/decoder_builder.py @@ -0,0 +1,72 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""DataDecoder builder. + +Creates DataDecoders from InputReader configs. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from object_detection.data_decoders import tf_example_decoder +from object_detection.data_decoders import tf_sequence_example_decoder +from object_detection.protos import input_reader_pb2 + + +def build(input_reader_config): + """Builds a DataDecoder based only on the open source config proto. + + Args: + input_reader_config: An input_reader_pb2.InputReader object. + + Returns: + A DataDecoder based on the input_reader_config. + + Raises: + ValueError: On invalid input reader proto. + """ + if not isinstance(input_reader_config, input_reader_pb2.InputReader): + raise ValueError('input_reader_config not of type ' + 'input_reader_pb2.InputReader.') + + if input_reader_config.WhichOneof('input_reader') == 'tf_record_input_reader': + label_map_proto_file = None + if input_reader_config.HasField('label_map_path'): + label_map_proto_file = input_reader_config.label_map_path + input_type = input_reader_config.input_type + if input_type == input_reader_pb2.InputType.Value('TF_EXAMPLE'): + decoder = tf_example_decoder.TfExampleDecoder( + load_instance_masks=input_reader_config.load_instance_masks, + load_multiclass_scores=input_reader_config.load_multiclass_scores, + load_context_features=input_reader_config.load_context_features, + instance_mask_type=input_reader_config.mask_type, + label_map_proto_file=label_map_proto_file, + use_display_name=input_reader_config.use_display_name, + num_additional_channels=input_reader_config.num_additional_channels, + num_keypoints=input_reader_config.num_keypoints, + expand_hierarchy_labels=input_reader_config.expand_labels_hierarchy, + load_dense_pose=input_reader_config.load_dense_pose, + load_track_id=input_reader_config.load_track_id) + return decoder + elif input_type == input_reader_pb2.InputType.Value('TF_SEQUENCE_EXAMPLE'): + decoder = tf_sequence_example_decoder.TfSequenceExampleDecoder( + label_map_proto_file=label_map_proto_file, + load_context_features=input_reader_config.load_context_features) + return decoder + raise ValueError('Unsupported input_type in config.') + + raise ValueError('Unsupported input_reader_config.') diff --git a/workspace/virtuallab/object_detection/builders/decoder_builder.pyc b/workspace/virtuallab/object_detection/builders/decoder_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77f2ff49a04fcf5b89ac4306632daf6b98ca9ea2 GIT binary patch literal 2231 zcmb_d&2A$_5Uv?JcI^B(zq>&oXbwm|*%Bud!HSUeij^P>lPtUPrD;#M*W=7g57RxX zSV}I0GY`O9@K)S;0jj#k8)u0)VQXr-x~i*czWV9L-&@^3fB%EWQ2#=FfBka+#^6E% zjFAQ)Lr4S2LKue-5Z`co1I7){Hyz)Eanti_kgh@2f^p09EyuTE-1dAM(sjr>Fzz6~ z4!@zJF31j?_q*uVjF3DPRyPloqs<`yg`7^|V|PJry2T#IEw|VQ zx$PDQAbW0c2y(|Q9>Fba+%(|20q6JP*j>i)Nnjtt{AlT8>H8MK`WS(+N7^0?V)ttn zF*xrZB9^9&IDSKwW0+Vi7&Z|`Nv893frQzaV!BW{GrB5cRTmE{N^Lb(9AhWt8*_|r z#_r|L7uhj8$?xxDP6w;Ss3&t3TW2X!wkq@5*vW%2xo{rvNQVFdaz6U;<>>5mv_u9w9pGhds)8mImZ=A}PEcK(1H&HFs;7#t%)wdz1JrWJ z<8Z5Sj@D>z1IhEdf$4Pa3d*ssW=TAIkt;p5G@Z_Lt}KnpZ-sf5s8mM8c;jxOImkOp zyBCxIa^4bmmpFQy|uJ?qgPQC&n7PDBM}$pBS?%V0z^JECLJE(s|`HRvAdJM1GW zsE9HiQMC35$;I@%p2qim_MHYDSS?!aQH!W{qmRf&8_9CU;}ZHrTqg}@I#a{Bo(wa* zpHegQJ&rem#a74S8P+$J8P*2)@(L>!4i?9LO!mGYtDAd4uW=Z(f?lv0bb=#%mjBkf zRR?8FCu_M?FFflLaxPHLrH(mw3E_BeQD<--k@1Lbn22t6OCH~kbs+JkU0!C!rOIPS raQ{o$jYZ?CDEbT)94L3>e}IST>Py76O4SoelA(;W!kqyBo;Lmg`cg(6 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/decoder_builder_test.py b/workspace/virtuallab/object_detection/builders/decoder_builder_test.py new file mode 100644 index 0000000..d45285f --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/decoder_builder_test.py @@ -0,0 +1,193 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for decoder_builder.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import numpy as np +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format +from object_detection.builders import decoder_builder +from object_detection.core import standard_fields as fields +from object_detection.dataset_tools import seq_example_util +from object_detection.protos import input_reader_pb2 +from object_detection.utils import dataset_util +from object_detection.utils import test_case + + +def _get_labelmap_path(): + """Returns an absolute path to label map file.""" + parent_path = os.path.dirname(tf.resource_loader.get_data_files_path()) + return os.path.join(parent_path, 'data', + 'pet_label_map.pbtxt') + + +class DecoderBuilderTest(test_case.TestCase): + + def _make_serialized_tf_example(self, has_additional_channels=False): + image_tensor_np = np.random.randint(255, size=(4, 5, 3)).astype(np.uint8) + additional_channels_tensor_np = np.random.randint( + 255, size=(4, 5, 1)).astype(np.uint8) + flat_mask = (4 * 5) * [1.0] + def graph_fn(image_tensor): + encoded_jpeg = tf.image.encode_jpeg(image_tensor) + return encoded_jpeg + encoded_jpeg = self.execute_cpu(graph_fn, [image_tensor_np]) + encoded_additional_channels_jpeg = self.execute_cpu( + graph_fn, [additional_channels_tensor_np]) + + features = { + 'image/source_id': dataset_util.bytes_feature('0'.encode()), + 'image/encoded': dataset_util.bytes_feature(encoded_jpeg), + 'image/format': dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/height': dataset_util.int64_feature(4), + 'image/width': dataset_util.int64_feature(5), + 'image/object/bbox/xmin': dataset_util.float_list_feature([0.0]), + 'image/object/bbox/xmax': dataset_util.float_list_feature([1.0]), + 'image/object/bbox/ymin': dataset_util.float_list_feature([0.0]), + 'image/object/bbox/ymax': dataset_util.float_list_feature([1.0]), + 'image/object/class/label': dataset_util.int64_list_feature([2]), + 'image/object/mask': dataset_util.float_list_feature(flat_mask), + } + if has_additional_channels: + additional_channels_key = 'image/additional_channels/encoded' + features[additional_channels_key] = dataset_util.bytes_list_feature( + [encoded_additional_channels_jpeg] * 2) + example = tf.train.Example(features=tf.train.Features(feature=features)) + return example.SerializeToString() + + def _make_random_serialized_jpeg_images(self, num_frames, image_height, + image_width): + def graph_fn(): + images = tf.cast(tf.random.uniform( + [num_frames, image_height, image_width, 3], + maxval=256, + dtype=tf.int32), dtype=tf.uint8) + images_list = tf.unstack(images, axis=0) + encoded_images = [tf.io.encode_jpeg(image) for image in images_list] + return encoded_images + return self.execute_cpu(graph_fn, []) + + def _make_serialized_tf_sequence_example(self): + num_frames = 4 + image_height = 20 + image_width = 30 + image_source_ids = [str(i) for i in range(num_frames)] + encoded_images = self._make_random_serialized_jpeg_images( + num_frames, image_height, image_width) + sequence_example_serialized = seq_example_util.make_sequence_example( + dataset_name='video_dataset', + video_id='video', + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + image_source_ids=image_source_ids, + image_format='JPEG', + is_annotated=[[1], [1], [1], [1]], + bboxes=[ + [[]], # Frame 0. + [[0., 0., 1., 1.]], # Frame 1. + [[0., 0., 1., 1.], + [0.1, 0.1, 0.2, 0.2]], # Frame 2. + [[]], # Frame 3. + ], + label_strings=[ + [], # Frame 0. + ['Abyssinian'], # Frame 1. + ['Abyssinian', 'american_bulldog'], # Frame 2. + [], # Frame 3 + ]).SerializeToString() + return sequence_example_serialized + + def test_build_tf_record_input_reader(self): + input_reader_text_proto = 'tf_record_input_reader {}' + input_reader_proto = input_reader_pb2.InputReader() + text_format.Parse(input_reader_text_proto, input_reader_proto) + + decoder = decoder_builder.build(input_reader_proto) + serialized_seq_example = self._make_serialized_tf_example() + def graph_fn(): + tensor_dict = decoder.decode(serialized_seq_example) + return (tensor_dict[fields.InputDataFields.image], + tensor_dict[fields.InputDataFields.groundtruth_classes], + tensor_dict[fields.InputDataFields.groundtruth_boxes]) + + (image, groundtruth_classes, + groundtruth_boxes) = self.execute_cpu(graph_fn, []) + self.assertEqual((4, 5, 3), image.shape) + self.assertAllEqual([2], groundtruth_classes) + self.assertEqual((1, 4), groundtruth_boxes.shape) + self.assertAllEqual([0.0, 0.0, 1.0, 1.0], groundtruth_boxes[0]) + + def test_build_tf_record_input_reader_sequence_example(self): + label_map_path = _get_labelmap_path() + input_reader_text_proto = """ + input_type: TF_SEQUENCE_EXAMPLE + tf_record_input_reader {} + """ + input_reader_proto = input_reader_pb2.InputReader() + input_reader_proto.label_map_path = label_map_path + text_format.Parse(input_reader_text_proto, input_reader_proto) + + serialized_seq_example = self._make_serialized_tf_sequence_example() + def graph_fn(): + decoder = decoder_builder.build(input_reader_proto) + tensor_dict = decoder.decode(serialized_seq_example) + return (tensor_dict[fields.InputDataFields.image], + tensor_dict[fields.InputDataFields.groundtruth_classes], + tensor_dict[fields.InputDataFields.groundtruth_boxes], + tensor_dict[fields.InputDataFields.num_groundtruth_boxes]) + (actual_image, actual_groundtruth_classes, actual_groundtruth_boxes, + actual_num_groundtruth_boxes) = self.execute_cpu(graph_fn, []) + expected_groundtruth_classes = [[-1, -1], [1, -1], [1, 2], [-1, -1]] + expected_groundtruth_boxes = [[[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 1.0, 1.0], [0.1, 0.1, 0.2, 0.2]], + [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]]] + expected_num_groundtruth_boxes = [0, 1, 2, 0] + + # Sequence example images are encoded. + self.assertEqual((4,), actual_image.shape) + self.assertAllEqual(expected_groundtruth_classes, + actual_groundtruth_classes) + self.assertAllClose(expected_groundtruth_boxes, + actual_groundtruth_boxes) + self.assertAllClose( + expected_num_groundtruth_boxes, actual_num_groundtruth_boxes) + + def test_build_tf_record_input_reader_and_load_instance_masks(self): + input_reader_text_proto = """ + load_instance_masks: true + tf_record_input_reader {} + """ + input_reader_proto = input_reader_pb2.InputReader() + text_format.Parse(input_reader_text_proto, input_reader_proto) + + decoder = decoder_builder.build(input_reader_proto) + serialized_seq_example = self._make_serialized_tf_example() + def graph_fn(): + tensor_dict = decoder.decode(serialized_seq_example) + return tensor_dict[fields.InputDataFields.groundtruth_instance_masks] + masks = self.execute_cpu(graph_fn, []) + self.assertAllEqual((1, 4, 5), masks.shape) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/graph_rewriter_builder.py b/workspace/virtuallab/object_detection/builders/graph_rewriter_builder.py new file mode 100644 index 0000000..9cbeb4a --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/graph_rewriter_builder.py @@ -0,0 +1,53 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Functions for quantized training and evaluation.""" + +import tensorflow.compat.v1 as tf +import tf_slim as slim +# pylint: disable=g-import-not-at-top +try: + from tensorflow.contrib import quantize as contrib_quantize +except ImportError: + # TF 2.0 doesn't ship with contrib. + pass +# pylint: enable=g-import-not-at-top + + +def build(graph_rewriter_config, is_training): + """Returns a function that modifies default graph based on options. + + Args: + graph_rewriter_config: graph_rewriter_pb2.GraphRewriter proto. + is_training: whether in training of eval mode. + """ + def graph_rewrite_fn(): + """Function to quantize weights and activation of the default graph.""" + if (graph_rewriter_config.quantization.weight_bits != 8 or + graph_rewriter_config.quantization.activation_bits != 8): + raise ValueError('Only 8bit quantization is supported') + + # Quantize the graph by inserting quantize ops for weights and activations + if is_training: + contrib_quantize.experimental_create_training_graph( + input_graph=tf.get_default_graph(), + quant_delay=graph_rewriter_config.quantization.delay + ) + else: + contrib_quantize.experimental_create_eval_graph( + input_graph=tf.get_default_graph() + ) + slim.summarize_collection('quant_vars') + + return graph_rewrite_fn diff --git a/workspace/virtuallab/object_detection/builders/graph_rewriter_builder.pyc b/workspace/virtuallab/object_detection/builders/graph_rewriter_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..02cab7cce4fb231303528bc0b34a4613db491607 GIT binary patch literal 1594 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 max_dimension when keep_aspect_ratio_resizer + is used. + """ + if not isinstance(image_resizer_config, image_resizer_pb2.ImageResizer): + raise ValueError('image_resizer_config not of type ' + 'image_resizer_pb2.ImageResizer.') + + image_resizer_oneof = image_resizer_config.WhichOneof('image_resizer_oneof') + if image_resizer_oneof == 'keep_aspect_ratio_resizer': + keep_aspect_ratio_config = image_resizer_config.keep_aspect_ratio_resizer + if not (keep_aspect_ratio_config.min_dimension <= + keep_aspect_ratio_config.max_dimension): + raise ValueError('min_dimension > max_dimension') + method = _tf_resize_method(keep_aspect_ratio_config.resize_method) + per_channel_pad_value = (0, 0, 0) + if keep_aspect_ratio_config.per_channel_pad_value: + per_channel_pad_value = tuple(keep_aspect_ratio_config. + per_channel_pad_value) + image_resizer_fn = functools.partial( + preprocessor.resize_to_range, + min_dimension=keep_aspect_ratio_config.min_dimension, + max_dimension=keep_aspect_ratio_config.max_dimension, + method=method, + pad_to_max_dimension=keep_aspect_ratio_config.pad_to_max_dimension, + per_channel_pad_value=per_channel_pad_value) + if not keep_aspect_ratio_config.convert_to_grayscale: + return image_resizer_fn + elif image_resizer_oneof == 'fixed_shape_resizer': + fixed_shape_resizer_config = image_resizer_config.fixed_shape_resizer + method = _tf_resize_method(fixed_shape_resizer_config.resize_method) + image_resizer_fn = functools.partial( + preprocessor.resize_image, + new_height=fixed_shape_resizer_config.height, + new_width=fixed_shape_resizer_config.width, + method=method) + if not fixed_shape_resizer_config.convert_to_grayscale: + return image_resizer_fn + elif image_resizer_oneof == 'identity_resizer': + def image_resizer_fn(image, masks=None, **kwargs): + del kwargs + if masks is None: + return [image, tf.shape(image)] + else: + return [image, masks, tf.shape(image)] + return image_resizer_fn + elif image_resizer_oneof == 'conditional_shape_resizer': + conditional_shape_resize_config = ( + image_resizer_config.conditional_shape_resizer) + method = _tf_resize_method(conditional_shape_resize_config.resize_method) + + if conditional_shape_resize_config.condition == ( + image_resizer_pb2.ConditionalShapeResizer.GREATER): + image_resizer_fn = functools.partial( + preprocessor.resize_to_max_dimension, + max_dimension=conditional_shape_resize_config.size_threshold, + method=method) + + elif conditional_shape_resize_config.condition == ( + image_resizer_pb2.ConditionalShapeResizer.SMALLER): + image_resizer_fn = functools.partial( + preprocessor.resize_to_min_dimension, + min_dimension=conditional_shape_resize_config.size_threshold, + method=method) + else: + raise ValueError( + 'Invalid image resizer condition option for ' + 'ConditionalShapeResizer: \'%s\'.' + % conditional_shape_resize_config.condition) + if not conditional_shape_resize_config.convert_to_grayscale: + return image_resizer_fn + elif image_resizer_oneof == 'pad_to_multiple_resizer': + pad_to_multiple_resizer_config = ( + image_resizer_config.pad_to_multiple_resizer) + + if pad_to_multiple_resizer_config.multiple < 0: + raise ValueError('`multiple` for pad_to_multiple_resizer should be > 0.') + + else: + image_resizer_fn = functools.partial( + preprocessor.resize_pad_to_multiple, + multiple=pad_to_multiple_resizer_config.multiple) + + if not pad_to_multiple_resizer_config.convert_to_grayscale: + return image_resizer_fn + else: + raise ValueError( + 'Invalid image resizer option: \'%s\'.' % image_resizer_oneof) + + def grayscale_image_resizer(image, masks=None): + """Convert to grayscale before applying image_resizer_fn. + + Args: + image: A 3D tensor of shape [height, width, 3] + masks: (optional) rank 3 float32 tensor with shape [num_instances, height, + width] containing instance masks. + + Returns: + Note that the position of the resized_image_shape changes based on whether + masks are present. + resized_image: A 3D tensor of shape [new_height, new_width, 1], + where the image has been resized (with bilinear interpolation) so that + min(new_height, new_width) == min_dimension or + max(new_height, new_width) == max_dimension. + resized_masks: If masks is not None, also outputs masks. A 3D tensor of + shape [num_instances, new_height, new_width]. + resized_image_shape: A 1D tensor of shape [3] containing shape of the + resized image. + """ + # image_resizer_fn returns [resized_image, resized_image_shape] if + # mask==None, otherwise it returns + # [resized_image, resized_mask, resized_image_shape]. In either case, we + # only deal with first and last element of the returned list. + retval = image_resizer_fn(image, masks) + resized_image = retval[0] + resized_image_shape = retval[-1] + retval[0] = preprocessor.rgb_to_gray(resized_image) + retval[-1] = tf.concat([resized_image_shape[:-1], [1]], 0) + return retval + + return functools.partial(grayscale_image_resizer) diff --git a/workspace/virtuallab/object_detection/builders/image_resizer_builder.pyc b/workspace/virtuallab/object_detection/builders/image_resizer_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..183dd4bfed5c5b09b177ceb751b5e2e63d79b275 GIT binary patch literal 5579 zcmcIo-EJGl6`oy6q$o>~EZL5o#PK9S5V}yQI%tfUpPa49Z z8FM0D5TmAe(!~3`_!eRoMOf#?1rpY2HAu%M9{0yl9I9OQ$4SpbX(Ib+E~8PfuVk)t z^vfvOmuaT*fChCt`Vap0S`Jp4B|NfRWqI0DTBo_~y@+03@AG<<`&swX9xb4~)$na$%9?rMn7jvet*h?kBysEfQ$4ZL3K zm<4juD@Xi>o!4oR4f@Bh=uwdA*}W<`Qf82bvY)3Tsgm)i*taxCnUW@zk5z(={xnVx z+lz}*-plv(Hom3xmGbTSZTWamxUcMzfiZd19UCRnzC2JW^8=l!p7GhUrLSa~vp6aH z(F+y&dJtr)9AuTAGUGfcw9cyQr8yj)rAbI3n;I%T3Zr_&JF_B!NUK7^*Fij1J9(bw z+cN6QXEtWVKAy=)ley#MAW07s_Cg!>>C5ooS4C%K#a^q4Aw!WieKU_QP6bYtSvwFD zq~7ks-MyWAp2yj=iXM8~={)xLc6Pt`+5Im)Luq<{_rX{AAQgnb@IAAL#?D_cfi z#S(J?Jz)d~TPL;fD)^x(|27BdNNo<&?&c^BRjfDD?vSDpDuXYi`exT2482)R=Ayfu z9U01W-}H;{6nfr9r>4D#<(j+B|5w~4XTjwoK;EHa!zuVzSdL^zk{Uh&uUw%&5T-Uw z*MvuL&~avwz?9&~;hH#bMgC7(h{JL0ikCP(b$&L)fg^OEh0GN>4b{l4;$QQh!D~sq{*+VB3u+NN!R@EVJ+lb z6#Cch?}X?ie{;y(3vBKkhs<3Q$2injKR>l*s%%|^OFVQ*gv;ExTqt|x)JQs3O{G6z z>2I<0UpV1a5w3`D9f5hIoHVcU*qTe4v1MQfG}ot^seP55e@zVE;mQASrsljWXv>)M z-oy$Q(vyeWIazp}7yfHz;rr~g(+k%*E*FdCS1crvg6XCbj_?|b%-HZ7;uuym#83)g z`2o^=$E6%uFT?xpOt?M};iY2oE5{+1Vc}HzDjRT_bQId*w%-cThqJDT?~q{&g|L?k zB)-$ShGf-$LZUKHXxR(mIOxXef6zn~9IGd&mnQvaA8sHO4!(*nLxUfQ$)2)aMt>EwKGjr+An3Q7er?&EH*}r zNnCMC@F+_B$u{I&ISO9PydMr!ay~s4vJs!LRv}Z-=Sa}1Rd%Z~NeyuqGk0|Y>XG&? zC7)%aylvJ`3E{07%Kme?T3^TLYj+c2f-po)m1(wTcH5>-bC{vcB{Z`j^iAqlTw{oI zQHc4yL69UW_G!{{qHi|;jCz5I64~PqRJ1=ZrU?P`dKiUfV2GolP$ecZN98H(5tkA- z5!Zf$kG_q^ufQ+>2)JTFQGHrq~5mt7lH<2aOEg>&_THWyjtLjBRX23s==Q#b`EHWUy#7y2n4U{o7p)F`&# zKuLY=xJHDJ+T0Kbw!mc>T|_0_}NPZ;rMYjJ6(Y|DFc>nE~+XFvgGoBYO} zh7Eu#j=_f9dRm|gC*HPfS$72SO@?{7CHrw2n5|DsNes$`EL;)%DA6WJa0S|sh3G<{ zv?`v?(pfnfu!l4+Os~}RQtqX=C73|~Kj0bxGT;nkFq>Q}Lf^W<+Ch-8ue9t2fJKQ* z9AFe{OS)uTGJq3tUDG)77MROhOCjq#e@zi&gSg1T$A-N1bYr?t=pvH~s*{5NwkROE zEy+XKVyAVZI7(E2`(*+`&(fGLl{ckMdAk#FK+D#dT5rlbcVq=irxzwkO!B&Avw(Zj z0Y&V0`!+6g?;-vmwAzqC3qL(^#l z1q?usDXdUsGm%NC(qjRW3BY>~D;db&*yW25bAl1YVX*4+zhFqyElXToaTf8v?tJ7h z%UDm)2)FiHAEL=L8KG3O$JpLRY5DFxQnLi&yy}_4M`FL-k&s(7fF^WfPJi45E zgquueW(*`};^p60(lpk5r$tpu^_-0YNwB6aUjHw+ 0: + max_negatives_per_positive = config.max_negatives_per_positive + if config.num_hard_examples > 0: + num_hard_examples = config.num_hard_examples + hard_example_miner = losses.HardExampleMiner( + num_hard_examples=num_hard_examples, + iou_threshold=config.iou_threshold, + loss_type=loss_type, + cls_loss_weight=classification_weight, + loc_loss_weight=localization_weight, + max_negatives_per_positive=max_negatives_per_positive, + min_negatives_per_image=config.min_negatives_per_image) + return hard_example_miner + + +def build_faster_rcnn_classification_loss(loss_config): + """Builds a classification loss for Faster RCNN based on the loss config. + + Args: + loss_config: A losses_pb2.ClassificationLoss object. + + Returns: + Loss based on the config. + + Raises: + ValueError: On invalid loss_config. + """ + if not isinstance(loss_config, losses_pb2.ClassificationLoss): + raise ValueError('loss_config not of type losses_pb2.ClassificationLoss.') + + loss_type = loss_config.WhichOneof('classification_loss') + + if loss_type == 'weighted_sigmoid': + return losses.WeightedSigmoidClassificationLoss() + if loss_type == 'weighted_softmax': + config = loss_config.weighted_softmax + return losses.WeightedSoftmaxClassificationLoss( + logit_scale=config.logit_scale) + if loss_type == 'weighted_logits_softmax': + config = loss_config.weighted_logits_softmax + return losses.WeightedSoftmaxClassificationAgainstLogitsLoss( + logit_scale=config.logit_scale) + if loss_type == 'weighted_sigmoid_focal': + config = loss_config.weighted_sigmoid_focal + alpha = None + if config.HasField('alpha'): + alpha = config.alpha + return losses.SigmoidFocalClassificationLoss( + gamma=config.gamma, + alpha=alpha) + + # By default, Faster RCNN second stage classifier uses Softmax loss + # with anchor-wise outputs. + config = loss_config.weighted_softmax + return losses.WeightedSoftmaxClassificationLoss( + logit_scale=config.logit_scale) + + +def _build_localization_loss(loss_config): + """Builds a localization loss based on the loss config. + + Args: + loss_config: A losses_pb2.LocalizationLoss object. + + Returns: + Loss based on the config. + + Raises: + ValueError: On invalid loss_config. + """ + if not isinstance(loss_config, losses_pb2.LocalizationLoss): + raise ValueError('loss_config not of type losses_pb2.LocalizationLoss.') + + loss_type = loss_config.WhichOneof('localization_loss') + + if loss_type == 'weighted_l2': + return losses.WeightedL2LocalizationLoss() + + if loss_type == 'weighted_smooth_l1': + return losses.WeightedSmoothL1LocalizationLoss( + loss_config.weighted_smooth_l1.delta) + + if loss_type == 'weighted_iou': + return losses.WeightedIOULocalizationLoss() + + if loss_type == 'l1_localization_loss': + return losses.L1LocalizationLoss() + + raise ValueError('Empty loss config.') + + +def _build_classification_loss(loss_config): + """Builds a classification loss based on the loss config. + + Args: + loss_config: A losses_pb2.ClassificationLoss object. + + Returns: + Loss based on the config. + + Raises: + ValueError: On invalid loss_config. + """ + if not isinstance(loss_config, losses_pb2.ClassificationLoss): + raise ValueError('loss_config not of type losses_pb2.ClassificationLoss.') + + loss_type = loss_config.WhichOneof('classification_loss') + + if loss_type == 'weighted_sigmoid': + return losses.WeightedSigmoidClassificationLoss() + + if loss_type == 'weighted_sigmoid_focal': + config = loss_config.weighted_sigmoid_focal + alpha = None + if config.HasField('alpha'): + alpha = config.alpha + return losses.SigmoidFocalClassificationLoss( + gamma=config.gamma, + alpha=alpha) + + if loss_type == 'weighted_softmax': + config = loss_config.weighted_softmax + return losses.WeightedSoftmaxClassificationLoss( + logit_scale=config.logit_scale) + + if loss_type == 'weighted_logits_softmax': + config = loss_config.weighted_logits_softmax + return losses.WeightedSoftmaxClassificationAgainstLogitsLoss( + logit_scale=config.logit_scale) + + if loss_type == 'bootstrapped_sigmoid': + config = loss_config.bootstrapped_sigmoid + return losses.BootstrappedSigmoidClassificationLoss( + alpha=config.alpha, + bootstrap_type=('hard' if config.hard_bootstrap else 'soft')) + + if loss_type == 'penalty_reduced_logistic_focal_loss': + config = loss_config.penalty_reduced_logistic_focal_loss + return losses.PenaltyReducedLogisticFocalLoss( + alpha=config.alpha, beta=config.beta) + + raise ValueError('Empty loss config.') diff --git a/workspace/virtuallab/object_detection/builders/losses_builder.pyc b/workspace/virtuallab/object_detection/builders/losses_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3eb9a9b0a9b65e8d745da7130d85e41f46037953 GIT binary patch literal 7235 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/losses_builder_test.py b/workspace/virtuallab/object_detection/builders/losses_builder_test.py new file mode 100644 index 0000000..b37b7f3 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/losses_builder_test.py @@ -0,0 +1,558 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for losses_builder.""" + +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format +from object_detection.builders import losses_builder +from object_detection.core import losses +from object_detection.protos import losses_pb2 +from object_detection.utils import ops + + +class LocalizationLossBuilderTest(tf.test.TestCase): + + def test_build_weighted_l2_localization_loss(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, localization_loss, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(localization_loss, + losses.WeightedL2LocalizationLoss) + + def test_build_weighted_smooth_l1_localization_loss_default_delta(self): + losses_text_proto = """ + localization_loss { + weighted_smooth_l1 { + } + } + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, localization_loss, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(localization_loss, + losses.WeightedSmoothL1LocalizationLoss) + self.assertAlmostEqual(localization_loss._delta, 1.0) + + def test_build_weighted_smooth_l1_localization_loss_non_default_delta(self): + losses_text_proto = """ + localization_loss { + weighted_smooth_l1 { + delta: 0.1 + } + } + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, localization_loss, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(localization_loss, + losses.WeightedSmoothL1LocalizationLoss) + self.assertAlmostEqual(localization_loss._delta, 0.1) + + def test_build_weighted_iou_localization_loss(self): + losses_text_proto = """ + localization_loss { + weighted_iou { + } + } + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, localization_loss, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(localization_loss, + losses.WeightedIOULocalizationLoss) + + def test_anchorwise_output(self): + losses_text_proto = """ + localization_loss { + weighted_smooth_l1 { + } + } + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, localization_loss, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(localization_loss, + losses.WeightedSmoothL1LocalizationLoss) + predictions = tf.constant([[[0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]]]) + targets = tf.constant([[[0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]]]) + weights = tf.constant([[1.0, 1.0]]) + loss = localization_loss(predictions, targets, weights=weights) + self.assertEqual(loss.shape, [1, 2]) + + def test_raise_error_on_empty_localization_config(self): + losses_text_proto = """ + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + with self.assertRaises(ValueError): + losses_builder._build_localization_loss(losses_proto) + + + +class ClassificationLossBuilderTest(tf.test.TestCase): + + def test_build_weighted_sigmoid_classification_loss(self): + losses_text_proto = """ + classification_loss { + weighted_sigmoid { + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSigmoidClassificationLoss) + + def test_build_weighted_sigmoid_focal_classification_loss(self): + losses_text_proto = """ + classification_loss { + weighted_sigmoid_focal { + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.SigmoidFocalClassificationLoss) + self.assertAlmostEqual(classification_loss._alpha, None) + self.assertAlmostEqual(classification_loss._gamma, 2.0) + + def test_build_weighted_sigmoid_focal_loss_non_default(self): + losses_text_proto = """ + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 3.0 + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.SigmoidFocalClassificationLoss) + self.assertAlmostEqual(classification_loss._alpha, 0.25) + self.assertAlmostEqual(classification_loss._gamma, 3.0) + + def test_build_weighted_softmax_classification_loss(self): + losses_text_proto = """ + classification_loss { + weighted_softmax { + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + + def test_build_weighted_logits_softmax_classification_loss(self): + losses_text_proto = """ + classification_loss { + weighted_logits_softmax { + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance( + classification_loss, + losses.WeightedSoftmaxClassificationAgainstLogitsLoss) + + def test_build_weighted_softmax_classification_loss_with_logit_scale(self): + losses_text_proto = """ + classification_loss { + weighted_softmax { + logit_scale: 2.0 + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + + def test_build_bootstrapped_sigmoid_classification_loss(self): + losses_text_proto = """ + classification_loss { + bootstrapped_sigmoid { + alpha: 0.5 + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.BootstrappedSigmoidClassificationLoss) + + def test_anchorwise_output(self): + losses_text_proto = """ + classification_loss { + weighted_sigmoid { + anchorwise_output: true + } + } + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss, _, _, _, _, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSigmoidClassificationLoss) + predictions = tf.constant([[[0.0, 1.0, 0.0], [0.0, 0.5, 0.5]]]) + targets = tf.constant([[[0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]]) + weights = tf.constant([[[1.0, 1.0, 1.0], [1.0, 1.0, 1.0]]]) + loss = classification_loss(predictions, targets, weights=weights) + self.assertEqual(loss.shape, [1, 2, 3]) + + def test_raise_error_on_empty_config(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + with self.assertRaises(ValueError): + losses_builder.build(losses_proto) + + + +class HardExampleMinerBuilderTest(tf.test.TestCase): + + def test_do_not_build_hard_example_miner_by_default(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, _, _, _, hard_example_miner, _, _ = losses_builder.build(losses_proto) + self.assertEqual(hard_example_miner, None) + + def test_build_hard_example_miner_for_classification_loss(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + hard_example_miner { + loss_type: CLASSIFICATION + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, _, _, _, hard_example_miner, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(hard_example_miner, losses.HardExampleMiner) + self.assertEqual(hard_example_miner._loss_type, 'cls') + + def test_build_hard_example_miner_for_localization_loss(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + hard_example_miner { + loss_type: LOCALIZATION + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, _, _, _, hard_example_miner, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(hard_example_miner, losses.HardExampleMiner) + self.assertEqual(hard_example_miner._loss_type, 'loc') + + def test_build_hard_example_miner_with_non_default_values(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + hard_example_miner { + num_hard_examples: 32 + iou_threshold: 0.5 + loss_type: LOCALIZATION + max_negatives_per_positive: 10 + min_negatives_per_image: 3 + } + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + _, _, _, _, hard_example_miner, _, _ = losses_builder.build(losses_proto) + self.assertIsInstance(hard_example_miner, losses.HardExampleMiner) + self.assertEqual(hard_example_miner._num_hard_examples, 32) + self.assertAlmostEqual(hard_example_miner._iou_threshold, 0.5) + self.assertEqual(hard_example_miner._max_negatives_per_positive, 10) + self.assertEqual(hard_example_miner._min_negatives_per_image, 3) + + +class LossBuilderTest(tf.test.TestCase): + + def test_build_all_loss_parameters(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + hard_example_miner { + } + classification_weight: 0.8 + localization_weight: 0.2 + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + (classification_loss, localization_loss, classification_weight, + localization_weight, hard_example_miner, _, + _) = losses_builder.build(losses_proto) + self.assertIsInstance(hard_example_miner, losses.HardExampleMiner) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + self.assertIsInstance(localization_loss, + losses.WeightedL2LocalizationLoss) + self.assertAlmostEqual(classification_weight, 0.8) + self.assertAlmostEqual(localization_weight, 0.2) + + def test_build_expected_sampling(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + hard_example_miner { + } + classification_weight: 0.8 + localization_weight: 0.2 + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + (classification_loss, localization_loss, classification_weight, + localization_weight, hard_example_miner, _, + _) = losses_builder.build(losses_proto) + self.assertIsInstance(hard_example_miner, losses.HardExampleMiner) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + self.assertIsInstance(localization_loss, losses.WeightedL2LocalizationLoss) + self.assertAlmostEqual(classification_weight, 0.8) + self.assertAlmostEqual(localization_weight, 0.2) + + + def test_build_reweighting_unmatched_anchors(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_softmax { + } + } + hard_example_miner { + } + classification_weight: 0.8 + localization_weight: 0.2 + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + (classification_loss, localization_loss, classification_weight, + localization_weight, hard_example_miner, _, + _) = losses_builder.build(losses_proto) + self.assertIsInstance(hard_example_miner, losses.HardExampleMiner) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + self.assertIsInstance(localization_loss, losses.WeightedL2LocalizationLoss) + self.assertAlmostEqual(classification_weight, 0.8) + self.assertAlmostEqual(localization_weight, 0.2) + + def test_raise_error_when_both_focal_loss_and_hard_example_miner(self): + losses_text_proto = """ + localization_loss { + weighted_l2 { + } + } + classification_loss { + weighted_sigmoid_focal { + } + } + hard_example_miner { + } + classification_weight: 0.8 + localization_weight: 0.2 + """ + losses_proto = losses_pb2.Loss() + text_format.Merge(losses_text_proto, losses_proto) + with self.assertRaises(ValueError): + losses_builder.build(losses_proto) + + +class FasterRcnnClassificationLossBuilderTest(tf.test.TestCase): + + def test_build_sigmoid_loss(self): + losses_text_proto = """ + weighted_sigmoid { + } + """ + losses_proto = losses_pb2.ClassificationLoss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss = losses_builder.build_faster_rcnn_classification_loss( + losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSigmoidClassificationLoss) + + def test_build_softmax_loss(self): + losses_text_proto = """ + weighted_softmax { + } + """ + losses_proto = losses_pb2.ClassificationLoss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss = losses_builder.build_faster_rcnn_classification_loss( + losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + + def test_build_logits_softmax_loss(self): + losses_text_proto = """ + weighted_logits_softmax { + } + """ + losses_proto = losses_pb2.ClassificationLoss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss = losses_builder.build_faster_rcnn_classification_loss( + losses_proto) + self.assertTrue( + isinstance(classification_loss, + losses.WeightedSoftmaxClassificationAgainstLogitsLoss)) + + def test_build_sigmoid_focal_loss(self): + losses_text_proto = """ + weighted_sigmoid_focal { + } + """ + losses_proto = losses_pb2.ClassificationLoss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss = losses_builder.build_faster_rcnn_classification_loss( + losses_proto) + self.assertIsInstance(classification_loss, + losses.SigmoidFocalClassificationLoss) + + def test_build_softmax_loss_by_default(self): + losses_text_proto = """ + """ + losses_proto = losses_pb2.ClassificationLoss() + text_format.Merge(losses_text_proto, losses_proto) + classification_loss = losses_builder.build_faster_rcnn_classification_loss( + losses_proto) + self.assertIsInstance(classification_loss, + losses.WeightedSoftmaxClassificationLoss) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/matcher_builder.py b/workspace/virtuallab/object_detection/builders/matcher_builder.py new file mode 100644 index 0000000..086f74b --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/matcher_builder.py @@ -0,0 +1,58 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""A function to build an object detection matcher from configuration.""" + +from object_detection.matchers import argmax_matcher +from object_detection.protos import matcher_pb2 +from object_detection.utils import tf_version + +if tf_version.is_tf1(): + from object_detection.matchers import bipartite_matcher # pylint: disable=g-import-not-at-top + + +def build(matcher_config): + """Builds a matcher object based on the matcher config. + + Args: + matcher_config: A matcher.proto object containing the config for the desired + Matcher. + + Returns: + Matcher based on the config. + + Raises: + ValueError: On empty matcher proto. + """ + if not isinstance(matcher_config, matcher_pb2.Matcher): + raise ValueError('matcher_config not of type matcher_pb2.Matcher.') + if matcher_config.WhichOneof('matcher_oneof') == 'argmax_matcher': + matcher = matcher_config.argmax_matcher + matched_threshold = unmatched_threshold = None + if not matcher.ignore_thresholds: + matched_threshold = matcher.matched_threshold + unmatched_threshold = matcher.unmatched_threshold + return argmax_matcher.ArgMaxMatcher( + matched_threshold=matched_threshold, + unmatched_threshold=unmatched_threshold, + negatives_lower_than_unmatched=matcher.negatives_lower_than_unmatched, + force_match_for_each_row=matcher.force_match_for_each_row, + use_matmul_gather=matcher.use_matmul_gather) + if matcher_config.WhichOneof('matcher_oneof') == 'bipartite_matcher': + if tf_version.is_tf2(): + raise ValueError('bipartite_matcher is not supported in TF 2.X') + matcher = matcher_config.bipartite_matcher + return bipartite_matcher.GreedyBipartiteMatcher(matcher.use_matmul_gather) + raise ValueError('Empty matcher.') diff --git a/workspace/virtuallab/object_detection/builders/matcher_builder.pyc b/workspace/virtuallab/object_detection/builders/matcher_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71f3a6cf351d5f7e6c0abf87867133ea022442dd GIT binary patch literal 1741 zcmb_c&5qkP5FW}uYkR$olOX*&6zt81LPqzvXo7Bvw5MzUZU5klAWO7OM-%R z9eIaP9KiW>K)Jp{bIO;s%AM4TJIxohEDN3~t`{4TJ6;HfFLG9y%h$s2rO_47wOYzm zZ8CDj@*n(78Irk=&}3#+Wv|jOp}`Lk4?wzI95>*IW-QYe!dRqfu)ByYUXC$`1yBAR$9?A&cw5W&}kSE+Z&EtSC5b z(2oF`#!727DG`CuxOUr3x zw%%^Fak#ru@gKkE$MLV04%?IW1&Yt6_YiY*u%wmBx=iIF@h9^Du^@%_?}hfCzpQ1x zezsSIPTLu({Vb-C{sfBOEM%AQ51zro4Vz(U#t5V zzke`76c>-%!ycwjDYL&v8u3kNQAe}^+NYl)2;SI-IGU|>C1xAFm{qzErJXfxobIl% znZMxh!kM+~0f@I3c+hbJ=wyA41M69j?y)=U0UNRrK0`Lfr;mRcj}RZDcgQB}Z0hea zDltt9ou{e$496d4k3&-1blxZb72= 3: + from object_detection.models import ssd_efficientnet_bifpn_feature_extractor as ssd_efficientnet_bifpn + +if tf_version.is_tf1(): + from object_detection.models import faster_rcnn_inception_resnet_v2_feature_extractor as frcnn_inc_res + from object_detection.models import faster_rcnn_inception_v2_feature_extractor as frcnn_inc_v2 + from object_detection.models import faster_rcnn_nas_feature_extractor as frcnn_nas + from object_detection.models import faster_rcnn_pnas_feature_extractor as frcnn_pnas + from object_detection.models import faster_rcnn_resnet_v1_feature_extractor as frcnn_resnet_v1 + from object_detection.models import ssd_resnet_v1_fpn_feature_extractor as ssd_resnet_v1_fpn + from object_detection.models import ssd_resnet_v1_ppn_feature_extractor as ssd_resnet_v1_ppn + from object_detection.models.embedded_ssd_mobilenet_v1_feature_extractor import EmbeddedSSDMobileNetV1FeatureExtractor + from object_detection.models.ssd_inception_v2_feature_extractor import SSDInceptionV2FeatureExtractor + from object_detection.models.ssd_mobilenet_v2_fpn_feature_extractor import SSDMobileNetV2FpnFeatureExtractor + from object_detection.models.ssd_mobilenet_v2_mnasfpn_feature_extractor import SSDMobileNetV2MnasFPNFeatureExtractor + from object_detection.models.ssd_inception_v3_feature_extractor import SSDInceptionV3FeatureExtractor + from object_detection.models.ssd_mobilenet_edgetpu_feature_extractor import SSDMobileNetEdgeTPUFeatureExtractor + from object_detection.models.ssd_mobilenet_v1_feature_extractor import SSDMobileNetV1FeatureExtractor + from object_detection.models.ssd_mobilenet_v1_fpn_feature_extractor import SSDMobileNetV1FpnFeatureExtractor + from object_detection.models.ssd_mobilenet_v1_ppn_feature_extractor import SSDMobileNetV1PpnFeatureExtractor + from object_detection.models.ssd_mobilenet_v2_feature_extractor import SSDMobileNetV2FeatureExtractor + from object_detection.models.ssd_mobilenet_v3_feature_extractor import SSDMobileNetV3LargeFeatureExtractor + from object_detection.models.ssd_mobilenet_v3_feature_extractor import SSDMobileNetV3SmallFeatureExtractor + from object_detection.models.ssd_mobiledet_feature_extractor import SSDMobileDetCPUFeatureExtractor + from object_detection.models.ssd_mobiledet_feature_extractor import SSDMobileDetDSPFeatureExtractor + from object_detection.models.ssd_mobiledet_feature_extractor import SSDMobileDetEdgeTPUFeatureExtractor + from object_detection.models.ssd_mobiledet_feature_extractor import SSDMobileDetGPUFeatureExtractor + from object_detection.models.ssd_pnasnet_feature_extractor import SSDPNASNetFeatureExtractor + from object_detection.predictors import rfcn_box_predictor +# pylint: enable=g-import-not-at-top + +if tf_version.is_tf2(): + SSD_KERAS_FEATURE_EXTRACTOR_CLASS_MAP = { + 'ssd_mobilenet_v1_keras': SSDMobileNetV1KerasFeatureExtractor, + 'ssd_mobilenet_v1_fpn_keras': SSDMobileNetV1FpnKerasFeatureExtractor, + 'ssd_mobilenet_v2_keras': SSDMobileNetV2KerasFeatureExtractor, + 'ssd_mobilenet_v2_fpn_keras': SSDMobileNetV2FpnKerasFeatureExtractor, + 'ssd_resnet50_v1_fpn_keras': + ssd_resnet_v1_fpn_keras.SSDResNet50V1FpnKerasFeatureExtractor, + 'ssd_resnet101_v1_fpn_keras': + ssd_resnet_v1_fpn_keras.SSDResNet101V1FpnKerasFeatureExtractor, + 'ssd_resnet152_v1_fpn_keras': + ssd_resnet_v1_fpn_keras.SSDResNet152V1FpnKerasFeatureExtractor, + 'ssd_efficientnet-b0_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB0BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b1_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB1BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b2_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB2BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b3_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB3BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b4_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB4BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b5_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB5BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b6_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB6BiFPNKerasFeatureExtractor, + 'ssd_efficientnet-b7_bifpn_keras': + ssd_efficientnet_bifpn.SSDEfficientNetB7BiFPNKerasFeatureExtractor, + } + + FASTER_RCNN_KERAS_FEATURE_EXTRACTOR_CLASS_MAP = { + 'faster_rcnn_resnet50_keras': + frcnn_resnet_keras.FasterRCNNResnet50KerasFeatureExtractor, + 'faster_rcnn_resnet101_keras': + frcnn_resnet_keras.FasterRCNNResnet101KerasFeatureExtractor, + 'faster_rcnn_resnet152_keras': + frcnn_resnet_keras.FasterRCNNResnet152KerasFeatureExtractor, + 'faster_rcnn_inception_resnet_v2_keras': + frcnn_inc_res_keras.FasterRCNNInceptionResnetV2KerasFeatureExtractor, + 'faster_rcnn_resnet50_fpn_keras': + frcnn_resnet_fpn_keras.FasterRCNNResnet50FpnKerasFeatureExtractor, + 'faster_rcnn_resnet101_fpn_keras': + frcnn_resnet_fpn_keras.FasterRCNNResnet101FpnKerasFeatureExtractor, + 'faster_rcnn_resnet152_fpn_keras': + frcnn_resnet_fpn_keras.FasterRCNNResnet152FpnKerasFeatureExtractor, + } + + CENTER_NET_EXTRACTOR_FUNCTION_MAP = { + 'resnet_v2_50': + center_net_resnet_feature_extractor.resnet_v2_50, + 'resnet_v2_101': + center_net_resnet_feature_extractor.resnet_v2_101, + 'resnet_v1_18_fpn': + center_net_resnet_v1_fpn_feature_extractor.resnet_v1_18_fpn, + 'resnet_v1_34_fpn': + center_net_resnet_v1_fpn_feature_extractor.resnet_v1_34_fpn, + 'resnet_v1_50_fpn': + center_net_resnet_v1_fpn_feature_extractor.resnet_v1_50_fpn, + 'resnet_v1_101_fpn': + center_net_resnet_v1_fpn_feature_extractor.resnet_v1_101_fpn, + 'hourglass_104': + center_net_hourglass_feature_extractor.hourglass_104, + 'mobilenet_v2': + center_net_mobilenet_v2_feature_extractor.mobilenet_v2, + 'mobilenet_v2_fpn': + center_net_mobilenet_v2_fpn_feature_extractor.mobilenet_v2_fpn, + } + + FEATURE_EXTRACTOR_MAPS = [ + CENTER_NET_EXTRACTOR_FUNCTION_MAP, + FASTER_RCNN_KERAS_FEATURE_EXTRACTOR_CLASS_MAP, + SSD_KERAS_FEATURE_EXTRACTOR_CLASS_MAP + ] + +if tf_version.is_tf1(): + SSD_FEATURE_EXTRACTOR_CLASS_MAP = { + 'ssd_inception_v2': + SSDInceptionV2FeatureExtractor, + 'ssd_inception_v3': + SSDInceptionV3FeatureExtractor, + 'ssd_mobilenet_v1': + SSDMobileNetV1FeatureExtractor, + 'ssd_mobilenet_v1_fpn': + SSDMobileNetV1FpnFeatureExtractor, + 'ssd_mobilenet_v1_ppn': + SSDMobileNetV1PpnFeatureExtractor, + 'ssd_mobilenet_v2': + SSDMobileNetV2FeatureExtractor, + 'ssd_mobilenet_v2_fpn': + SSDMobileNetV2FpnFeatureExtractor, + 'ssd_mobilenet_v2_mnasfpn': + SSDMobileNetV2MnasFPNFeatureExtractor, + 'ssd_mobilenet_v3_large': + SSDMobileNetV3LargeFeatureExtractor, + 'ssd_mobilenet_v3_small': + SSDMobileNetV3SmallFeatureExtractor, + 'ssd_mobilenet_edgetpu': + SSDMobileNetEdgeTPUFeatureExtractor, + 'ssd_resnet50_v1_fpn': + ssd_resnet_v1_fpn.SSDResnet50V1FpnFeatureExtractor, + 'ssd_resnet101_v1_fpn': + ssd_resnet_v1_fpn.SSDResnet101V1FpnFeatureExtractor, + 'ssd_resnet152_v1_fpn': + ssd_resnet_v1_fpn.SSDResnet152V1FpnFeatureExtractor, + 'ssd_resnet50_v1_ppn': + ssd_resnet_v1_ppn.SSDResnet50V1PpnFeatureExtractor, + 'ssd_resnet101_v1_ppn': + ssd_resnet_v1_ppn.SSDResnet101V1PpnFeatureExtractor, + 'ssd_resnet152_v1_ppn': + ssd_resnet_v1_ppn.SSDResnet152V1PpnFeatureExtractor, + 'embedded_ssd_mobilenet_v1': + EmbeddedSSDMobileNetV1FeatureExtractor, + 'ssd_pnasnet': + SSDPNASNetFeatureExtractor, + 'ssd_mobiledet_cpu': + SSDMobileDetCPUFeatureExtractor, + 'ssd_mobiledet_dsp': + SSDMobileDetDSPFeatureExtractor, + 'ssd_mobiledet_edgetpu': + SSDMobileDetEdgeTPUFeatureExtractor, + 'ssd_mobiledet_gpu': + SSDMobileDetGPUFeatureExtractor, + } + + FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP = { + 'faster_rcnn_nas': + frcnn_nas.FasterRCNNNASFeatureExtractor, + 'faster_rcnn_pnas': + frcnn_pnas.FasterRCNNPNASFeatureExtractor, + 'faster_rcnn_inception_resnet_v2': + frcnn_inc_res.FasterRCNNInceptionResnetV2FeatureExtractor, + 'faster_rcnn_inception_v2': + frcnn_inc_v2.FasterRCNNInceptionV2FeatureExtractor, + 'faster_rcnn_resnet50': + frcnn_resnet_v1.FasterRCNNResnet50FeatureExtractor, + 'faster_rcnn_resnet101': + frcnn_resnet_v1.FasterRCNNResnet101FeatureExtractor, + 'faster_rcnn_resnet152': + frcnn_resnet_v1.FasterRCNNResnet152FeatureExtractor, + } + + FEATURE_EXTRACTOR_MAPS = [ + SSD_FEATURE_EXTRACTOR_CLASS_MAP, + FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP + ] + + +def _check_feature_extractor_exists(feature_extractor_type): + feature_extractors = set().union(*FEATURE_EXTRACTOR_MAPS) + if feature_extractor_type not in feature_extractors: + raise ValueError('{} is not supported. See `model_builder.py` for features ' + 'extractors compatible with different versions of ' + 'Tensorflow'.format(feature_extractor_type)) + + +def _build_ssd_feature_extractor(feature_extractor_config, + is_training, + freeze_batchnorm, + reuse_weights=None): + """Builds a ssd_meta_arch.SSDFeatureExtractor based on config. + + Args: + feature_extractor_config: A SSDFeatureExtractor proto config from ssd.proto. + is_training: True if this feature extractor is being built for training. + freeze_batchnorm: Whether to freeze batch norm parameters during + training or not. When training with a small batch size (e.g. 1), it is + desirable to freeze batch norm update and use pretrained batch norm + params. + reuse_weights: if the feature extractor should reuse weights. + + Returns: + ssd_meta_arch.SSDFeatureExtractor based on config. + + Raises: + ValueError: On invalid feature extractor type. + """ + feature_type = feature_extractor_config.type + depth_multiplier = feature_extractor_config.depth_multiplier + min_depth = feature_extractor_config.min_depth + pad_to_multiple = feature_extractor_config.pad_to_multiple + use_explicit_padding = feature_extractor_config.use_explicit_padding + use_depthwise = feature_extractor_config.use_depthwise + + is_keras = tf_version.is_tf2() + if is_keras: + conv_hyperparams = hyperparams_builder.KerasLayerHyperparams( + feature_extractor_config.conv_hyperparams) + else: + conv_hyperparams = hyperparams_builder.build( + feature_extractor_config.conv_hyperparams, is_training) + override_base_feature_extractor_hyperparams = ( + feature_extractor_config.override_base_feature_extractor_hyperparams) + + if not is_keras and feature_type not in SSD_FEATURE_EXTRACTOR_CLASS_MAP: + raise ValueError('Unknown ssd feature_extractor: {}'.format(feature_type)) + + if is_keras: + feature_extractor_class = SSD_KERAS_FEATURE_EXTRACTOR_CLASS_MAP[ + feature_type] + else: + feature_extractor_class = SSD_FEATURE_EXTRACTOR_CLASS_MAP[feature_type] + kwargs = { + 'is_training': + is_training, + 'depth_multiplier': + depth_multiplier, + 'min_depth': + min_depth, + 'pad_to_multiple': + pad_to_multiple, + 'use_explicit_padding': + use_explicit_padding, + 'use_depthwise': + use_depthwise, + 'override_base_feature_extractor_hyperparams': + override_base_feature_extractor_hyperparams + } + + if feature_extractor_config.HasField('replace_preprocessor_with_placeholder'): + kwargs.update({ + 'replace_preprocessor_with_placeholder': + feature_extractor_config.replace_preprocessor_with_placeholder + }) + + if feature_extractor_config.HasField('num_layers'): + kwargs.update({'num_layers': feature_extractor_config.num_layers}) + + if is_keras: + kwargs.update({ + 'conv_hyperparams': conv_hyperparams, + 'inplace_batchnorm_update': False, + 'freeze_batchnorm': freeze_batchnorm + }) + else: + kwargs.update({ + 'conv_hyperparams_fn': conv_hyperparams, + 'reuse_weights': reuse_weights, + }) + + + if feature_extractor_config.HasField('fpn'): + kwargs.update({ + 'fpn_min_level': + feature_extractor_config.fpn.min_level, + 'fpn_max_level': + feature_extractor_config.fpn.max_level, + 'additional_layer_depth': + feature_extractor_config.fpn.additional_layer_depth, + }) + + if feature_extractor_config.HasField('bifpn'): + kwargs.update({ + 'bifpn_min_level': feature_extractor_config.bifpn.min_level, + 'bifpn_max_level': feature_extractor_config.bifpn.max_level, + 'bifpn_num_iterations': feature_extractor_config.bifpn.num_iterations, + 'bifpn_num_filters': feature_extractor_config.bifpn.num_filters, + 'bifpn_combine_method': feature_extractor_config.bifpn.combine_method, + }) + + return feature_extractor_class(**kwargs) + + +def _build_ssd_model(ssd_config, is_training, add_summaries): + """Builds an SSD detection model based on the model config. + + Args: + ssd_config: A ssd.proto object containing the config for the desired + SSDMetaArch. + is_training: True if this model is being built for training purposes. + add_summaries: Whether to add tf summaries in the model. + Returns: + SSDMetaArch based on the config. + + Raises: + ValueError: If ssd_config.type is not recognized (i.e. not registered in + model_class_map). + """ + num_classes = ssd_config.num_classes + _check_feature_extractor_exists(ssd_config.feature_extractor.type) + + # Feature extractor + feature_extractor = _build_ssd_feature_extractor( + feature_extractor_config=ssd_config.feature_extractor, + freeze_batchnorm=ssd_config.freeze_batchnorm, + is_training=is_training) + + box_coder = box_coder_builder.build(ssd_config.box_coder) + matcher = matcher_builder.build(ssd_config.matcher) + region_similarity_calculator = sim_calc.build( + ssd_config.similarity_calculator) + encode_background_as_zeros = ssd_config.encode_background_as_zeros + negative_class_weight = ssd_config.negative_class_weight + anchor_generator = anchor_generator_builder.build( + ssd_config.anchor_generator) + if feature_extractor.is_keras_model: + ssd_box_predictor = box_predictor_builder.build_keras( + hyperparams_fn=hyperparams_builder.KerasLayerHyperparams, + freeze_batchnorm=ssd_config.freeze_batchnorm, + inplace_batchnorm_update=False, + num_predictions_per_location_list=anchor_generator + .num_anchors_per_location(), + box_predictor_config=ssd_config.box_predictor, + is_training=is_training, + num_classes=num_classes, + add_background_class=ssd_config.add_background_class) + else: + ssd_box_predictor = box_predictor_builder.build( + hyperparams_builder.build, ssd_config.box_predictor, is_training, + num_classes, ssd_config.add_background_class) + image_resizer_fn = image_resizer_builder.build(ssd_config.image_resizer) + non_max_suppression_fn, score_conversion_fn = post_processing_builder.build( + ssd_config.post_processing) + (classification_loss, localization_loss, classification_weight, + localization_weight, hard_example_miner, random_example_sampler, + expected_loss_weights_fn) = losses_builder.build(ssd_config.loss) + normalize_loss_by_num_matches = ssd_config.normalize_loss_by_num_matches + normalize_loc_loss_by_codesize = ssd_config.normalize_loc_loss_by_codesize + + equalization_loss_config = ops.EqualizationLossConfig( + weight=ssd_config.loss.equalization_loss.weight, + exclude_prefixes=ssd_config.loss.equalization_loss.exclude_prefixes) + + target_assigner_instance = target_assigner.TargetAssigner( + region_similarity_calculator, + matcher, + box_coder, + negative_class_weight=negative_class_weight) + + ssd_meta_arch_fn = ssd_meta_arch.SSDMetaArch + kwargs = {} + + return ssd_meta_arch_fn( + is_training=is_training, + anchor_generator=anchor_generator, + box_predictor=ssd_box_predictor, + box_coder=box_coder, + feature_extractor=feature_extractor, + encode_background_as_zeros=encode_background_as_zeros, + image_resizer_fn=image_resizer_fn, + non_max_suppression_fn=non_max_suppression_fn, + score_conversion_fn=score_conversion_fn, + classification_loss=classification_loss, + localization_loss=localization_loss, + classification_loss_weight=classification_weight, + localization_loss_weight=localization_weight, + normalize_loss_by_num_matches=normalize_loss_by_num_matches, + hard_example_miner=hard_example_miner, + target_assigner_instance=target_assigner_instance, + add_summaries=add_summaries, + normalize_loc_loss_by_codesize=normalize_loc_loss_by_codesize, + freeze_batchnorm=ssd_config.freeze_batchnorm, + inplace_batchnorm_update=ssd_config.inplace_batchnorm_update, + add_background_class=ssd_config.add_background_class, + explicit_background_class=ssd_config.explicit_background_class, + random_example_sampler=random_example_sampler, + expected_loss_weights_fn=expected_loss_weights_fn, + use_confidences_as_targets=ssd_config.use_confidences_as_targets, + implicit_example_weight=ssd_config.implicit_example_weight, + equalization_loss_config=equalization_loss_config, + return_raw_detections_during_predict=( + ssd_config.return_raw_detections_during_predict), + **kwargs) + + +def _build_faster_rcnn_feature_extractor( + feature_extractor_config, is_training, reuse_weights=True, + inplace_batchnorm_update=False): + """Builds a faster_rcnn_meta_arch.FasterRCNNFeatureExtractor based on config. + + Args: + feature_extractor_config: A FasterRcnnFeatureExtractor proto config from + faster_rcnn.proto. + is_training: True if this feature extractor is being built for training. + reuse_weights: if the feature extractor should reuse weights. + inplace_batchnorm_update: Whether to update batch_norm inplace during + training. This is required for batch norm to work correctly on TPUs. When + this is false, user must add a control dependency on + tf.GraphKeys.UPDATE_OPS for train/loss op in order to update the batch + norm moving average parameters. + + Returns: + faster_rcnn_meta_arch.FasterRCNNFeatureExtractor based on config. + + Raises: + ValueError: On invalid feature extractor type. + """ + if inplace_batchnorm_update: + raise ValueError('inplace batchnorm updates not supported.') + feature_type = feature_extractor_config.type + first_stage_features_stride = ( + feature_extractor_config.first_stage_features_stride) + batch_norm_trainable = feature_extractor_config.batch_norm_trainable + + if feature_type not in FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP: + raise ValueError('Unknown Faster R-CNN feature_extractor: {}'.format( + feature_type)) + feature_extractor_class = FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP[ + feature_type] + return feature_extractor_class( + is_training, first_stage_features_stride, + batch_norm_trainable, reuse_weights=reuse_weights) + + +def _build_faster_rcnn_keras_feature_extractor( + feature_extractor_config, is_training, + inplace_batchnorm_update=False): + """Builds a faster_rcnn_meta_arch.FasterRCNNKerasFeatureExtractor from config. + + Args: + feature_extractor_config: A FasterRcnnFeatureExtractor proto config from + faster_rcnn.proto. + is_training: True if this feature extractor is being built for training. + inplace_batchnorm_update: Whether to update batch_norm inplace during + training. This is required for batch norm to work correctly on TPUs. When + this is false, user must add a control dependency on + tf.GraphKeys.UPDATE_OPS for train/loss op in order to update the batch + norm moving average parameters. + + Returns: + faster_rcnn_meta_arch.FasterRCNNKerasFeatureExtractor based on config. + + Raises: + ValueError: On invalid feature extractor type. + """ + if inplace_batchnorm_update: + raise ValueError('inplace batchnorm updates not supported.') + feature_type = feature_extractor_config.type + first_stage_features_stride = ( + feature_extractor_config.first_stage_features_stride) + batch_norm_trainable = feature_extractor_config.batch_norm_trainable + + if feature_type not in FASTER_RCNN_KERAS_FEATURE_EXTRACTOR_CLASS_MAP: + raise ValueError('Unknown Faster R-CNN feature_extractor: {}'.format( + feature_type)) + feature_extractor_class = FASTER_RCNN_KERAS_FEATURE_EXTRACTOR_CLASS_MAP[ + feature_type] + + kwargs = {} + + if feature_extractor_config.HasField('conv_hyperparams'): + kwargs.update({ + 'conv_hyperparams': + hyperparams_builder.KerasLayerHyperparams( + feature_extractor_config.conv_hyperparams), + 'override_base_feature_extractor_hyperparams': + feature_extractor_config.override_base_feature_extractor_hyperparams + }) + + if feature_extractor_config.HasField('fpn'): + kwargs.update({ + 'fpn_min_level': + feature_extractor_config.fpn.min_level, + 'fpn_max_level': + feature_extractor_config.fpn.max_level, + 'additional_layer_depth': + feature_extractor_config.fpn.additional_layer_depth, + }) + + return feature_extractor_class( + is_training, first_stage_features_stride, + batch_norm_trainable, **kwargs) + + +def _build_faster_rcnn_model(frcnn_config, is_training, add_summaries): + """Builds a Faster R-CNN or R-FCN detection model based on the model config. + + Builds R-FCN model if the second_stage_box_predictor in the config is of type + `rfcn_box_predictor` else builds a Faster R-CNN model. + + Args: + frcnn_config: A faster_rcnn.proto object containing the config for the + desired FasterRCNNMetaArch or RFCNMetaArch. + is_training: True if this model is being built for training purposes. + add_summaries: Whether to add tf summaries in the model. + + Returns: + FasterRCNNMetaArch based on the config. + + Raises: + ValueError: If frcnn_config.type is not recognized (i.e. not registered in + model_class_map). + """ + num_classes = frcnn_config.num_classes + image_resizer_fn = image_resizer_builder.build(frcnn_config.image_resizer) + _check_feature_extractor_exists(frcnn_config.feature_extractor.type) + is_keras = tf_version.is_tf2() + + if is_keras: + feature_extractor = _build_faster_rcnn_keras_feature_extractor( + frcnn_config.feature_extractor, is_training, + inplace_batchnorm_update=frcnn_config.inplace_batchnorm_update) + else: + feature_extractor = _build_faster_rcnn_feature_extractor( + frcnn_config.feature_extractor, is_training, + inplace_batchnorm_update=frcnn_config.inplace_batchnorm_update) + + number_of_stages = frcnn_config.number_of_stages + first_stage_anchor_generator = anchor_generator_builder.build( + frcnn_config.first_stage_anchor_generator) + + first_stage_target_assigner = target_assigner.create_target_assigner( + 'FasterRCNN', + 'proposal', + use_matmul_gather=frcnn_config.use_matmul_gather_in_matcher) + first_stage_atrous_rate = frcnn_config.first_stage_atrous_rate + if is_keras: + first_stage_box_predictor_arg_scope_fn = ( + hyperparams_builder.KerasLayerHyperparams( + frcnn_config.first_stage_box_predictor_conv_hyperparams)) + else: + first_stage_box_predictor_arg_scope_fn = hyperparams_builder.build( + frcnn_config.first_stage_box_predictor_conv_hyperparams, is_training) + first_stage_box_predictor_kernel_size = ( + frcnn_config.first_stage_box_predictor_kernel_size) + first_stage_box_predictor_depth = frcnn_config.first_stage_box_predictor_depth + first_stage_minibatch_size = frcnn_config.first_stage_minibatch_size + use_static_shapes = frcnn_config.use_static_shapes and ( + frcnn_config.use_static_shapes_for_eval or is_training) + first_stage_sampler = sampler.BalancedPositiveNegativeSampler( + positive_fraction=frcnn_config.first_stage_positive_balance_fraction, + is_static=(frcnn_config.use_static_balanced_label_sampler and + use_static_shapes)) + first_stage_max_proposals = frcnn_config.first_stage_max_proposals + if (frcnn_config.first_stage_nms_iou_threshold < 0 or + frcnn_config.first_stage_nms_iou_threshold > 1.0): + raise ValueError('iou_threshold not in [0, 1.0].') + if (is_training and frcnn_config.second_stage_batch_size > + first_stage_max_proposals): + raise ValueError('second_stage_batch_size should be no greater than ' + 'first_stage_max_proposals.') + first_stage_non_max_suppression_fn = functools.partial( + post_processing.batch_multiclass_non_max_suppression, + score_thresh=frcnn_config.first_stage_nms_score_threshold, + iou_thresh=frcnn_config.first_stage_nms_iou_threshold, + max_size_per_class=frcnn_config.first_stage_max_proposals, + max_total_size=frcnn_config.first_stage_max_proposals, + use_static_shapes=use_static_shapes, + use_partitioned_nms=frcnn_config.use_partitioned_nms_in_first_stage, + use_combined_nms=frcnn_config.use_combined_nms_in_first_stage) + first_stage_loc_loss_weight = ( + frcnn_config.first_stage_localization_loss_weight) + first_stage_obj_loss_weight = frcnn_config.first_stage_objectness_loss_weight + + initial_crop_size = frcnn_config.initial_crop_size + maxpool_kernel_size = frcnn_config.maxpool_kernel_size + maxpool_stride = frcnn_config.maxpool_stride + + second_stage_target_assigner = target_assigner.create_target_assigner( + 'FasterRCNN', + 'detection', + use_matmul_gather=frcnn_config.use_matmul_gather_in_matcher) + if is_keras: + second_stage_box_predictor = box_predictor_builder.build_keras( + hyperparams_builder.KerasLayerHyperparams, + freeze_batchnorm=False, + inplace_batchnorm_update=False, + num_predictions_per_location_list=[1], + box_predictor_config=frcnn_config.second_stage_box_predictor, + is_training=is_training, + num_classes=num_classes) + else: + second_stage_box_predictor = box_predictor_builder.build( + hyperparams_builder.build, + frcnn_config.second_stage_box_predictor, + is_training=is_training, + num_classes=num_classes) + second_stage_batch_size = frcnn_config.second_stage_batch_size + second_stage_sampler = sampler.BalancedPositiveNegativeSampler( + positive_fraction=frcnn_config.second_stage_balance_fraction, + is_static=(frcnn_config.use_static_balanced_label_sampler and + use_static_shapes)) + (second_stage_non_max_suppression_fn, second_stage_score_conversion_fn + ) = post_processing_builder.build(frcnn_config.second_stage_post_processing) + second_stage_localization_loss_weight = ( + frcnn_config.second_stage_localization_loss_weight) + second_stage_classification_loss = ( + losses_builder.build_faster_rcnn_classification_loss( + frcnn_config.second_stage_classification_loss)) + second_stage_classification_loss_weight = ( + frcnn_config.second_stage_classification_loss_weight) + second_stage_mask_prediction_loss_weight = ( + frcnn_config.second_stage_mask_prediction_loss_weight) + + hard_example_miner = None + if frcnn_config.HasField('hard_example_miner'): + hard_example_miner = losses_builder.build_hard_example_miner( + frcnn_config.hard_example_miner, + second_stage_classification_loss_weight, + second_stage_localization_loss_weight) + + crop_and_resize_fn = ( + spatial_ops.multilevel_matmul_crop_and_resize + if frcnn_config.use_matmul_crop_and_resize + else spatial_ops.multilevel_native_crop_and_resize) + clip_anchors_to_image = ( + frcnn_config.clip_anchors_to_image) + + common_kwargs = { + 'is_training': + is_training, + 'num_classes': + num_classes, + 'image_resizer_fn': + image_resizer_fn, + 'feature_extractor': + feature_extractor, + 'number_of_stages': + number_of_stages, + 'first_stage_anchor_generator': + first_stage_anchor_generator, + 'first_stage_target_assigner': + first_stage_target_assigner, + 'first_stage_atrous_rate': + first_stage_atrous_rate, + 'first_stage_box_predictor_arg_scope_fn': + first_stage_box_predictor_arg_scope_fn, + 'first_stage_box_predictor_kernel_size': + first_stage_box_predictor_kernel_size, + 'first_stage_box_predictor_depth': + first_stage_box_predictor_depth, + 'first_stage_minibatch_size': + first_stage_minibatch_size, + 'first_stage_sampler': + first_stage_sampler, + 'first_stage_non_max_suppression_fn': + first_stage_non_max_suppression_fn, + 'first_stage_max_proposals': + first_stage_max_proposals, + 'first_stage_localization_loss_weight': + first_stage_loc_loss_weight, + 'first_stage_objectness_loss_weight': + first_stage_obj_loss_weight, + 'second_stage_target_assigner': + second_stage_target_assigner, + 'second_stage_batch_size': + second_stage_batch_size, + 'second_stage_sampler': + second_stage_sampler, + 'second_stage_non_max_suppression_fn': + second_stage_non_max_suppression_fn, + 'second_stage_score_conversion_fn': + second_stage_score_conversion_fn, + 'second_stage_localization_loss_weight': + second_stage_localization_loss_weight, + 'second_stage_classification_loss': + second_stage_classification_loss, + 'second_stage_classification_loss_weight': + second_stage_classification_loss_weight, + 'hard_example_miner': + hard_example_miner, + 'add_summaries': + add_summaries, + 'crop_and_resize_fn': + crop_and_resize_fn, + 'clip_anchors_to_image': + clip_anchors_to_image, + 'use_static_shapes': + use_static_shapes, + 'resize_masks': + frcnn_config.resize_masks, + 'return_raw_detections_during_predict': + frcnn_config.return_raw_detections_during_predict, + 'output_final_box_features': + frcnn_config.output_final_box_features + } + + if ((not is_keras and isinstance(second_stage_box_predictor, + rfcn_box_predictor.RfcnBoxPredictor)) or + (is_keras and + isinstance(second_stage_box_predictor, + rfcn_keras_box_predictor.RfcnKerasBoxPredictor))): + return rfcn_meta_arch.RFCNMetaArch( + second_stage_rfcn_box_predictor=second_stage_box_predictor, + **common_kwargs) + elif frcnn_config.HasField('context_config'): + context_config = frcnn_config.context_config + common_kwargs.update({ + 'attention_bottleneck_dimension': + context_config.attention_bottleneck_dimension, + 'attention_temperature': + context_config.attention_temperature + }) + return context_rcnn_meta_arch.ContextRCNNMetaArch( + initial_crop_size=initial_crop_size, + maxpool_kernel_size=maxpool_kernel_size, + maxpool_stride=maxpool_stride, + second_stage_mask_rcnn_box_predictor=second_stage_box_predictor, + second_stage_mask_prediction_loss_weight=( + second_stage_mask_prediction_loss_weight), + **common_kwargs) + else: + return faster_rcnn_meta_arch.FasterRCNNMetaArch( + initial_crop_size=initial_crop_size, + maxpool_kernel_size=maxpool_kernel_size, + maxpool_stride=maxpool_stride, + second_stage_mask_rcnn_box_predictor=second_stage_box_predictor, + second_stage_mask_prediction_loss_weight=( + second_stage_mask_prediction_loss_weight), + **common_kwargs) + +EXPERIMENTAL_META_ARCH_BUILDER_MAP = { +} + + +def _build_experimental_model(config, is_training, add_summaries=True): + return EXPERIMENTAL_META_ARCH_BUILDER_MAP[config.name]( + is_training, add_summaries) + + +# The class ID in the groundtruth/model architecture is usually 0-based while +# the ID in the label map is 1-based. The offset is used to convert between the +# the two. +CLASS_ID_OFFSET = 1 +KEYPOINT_STD_DEV_DEFAULT = 1.0 + + +def keypoint_proto_to_params(kp_config, keypoint_map_dict): + """Converts CenterNet.KeypointEstimation proto to parameter namedtuple.""" + label_map_item = keypoint_map_dict[kp_config.keypoint_class_name] + + classification_loss, localization_loss, _, _, _, _, _ = ( + losses_builder.build(kp_config.loss)) + + keypoint_indices = [ + keypoint.id for keypoint in label_map_item.keypoints + ] + keypoint_labels = [ + keypoint.label for keypoint in label_map_item.keypoints + ] + keypoint_std_dev_dict = { + label: KEYPOINT_STD_DEV_DEFAULT for label in keypoint_labels + } + if kp_config.keypoint_label_to_std: + for label, value in kp_config.keypoint_label_to_std.items(): + keypoint_std_dev_dict[label] = value + keypoint_std_dev = [keypoint_std_dev_dict[label] for label in keypoint_labels] + return center_net_meta_arch.KeypointEstimationParams( + task_name=kp_config.task_name, + class_id=label_map_item.id - CLASS_ID_OFFSET, + keypoint_indices=keypoint_indices, + classification_loss=classification_loss, + localization_loss=localization_loss, + keypoint_labels=keypoint_labels, + keypoint_std_dev=keypoint_std_dev, + task_loss_weight=kp_config.task_loss_weight, + keypoint_regression_loss_weight=kp_config.keypoint_regression_loss_weight, + keypoint_heatmap_loss_weight=kp_config.keypoint_heatmap_loss_weight, + keypoint_offset_loss_weight=kp_config.keypoint_offset_loss_weight, + heatmap_bias_init=kp_config.heatmap_bias_init, + keypoint_candidate_score_threshold=( + kp_config.keypoint_candidate_score_threshold), + num_candidates_per_keypoint=kp_config.num_candidates_per_keypoint, + peak_max_pool_kernel_size=kp_config.peak_max_pool_kernel_size, + unmatched_keypoint_score=kp_config.unmatched_keypoint_score, + box_scale=kp_config.box_scale, + candidate_search_scale=kp_config.candidate_search_scale, + candidate_ranking_mode=kp_config.candidate_ranking_mode, + offset_peak_radius=kp_config.offset_peak_radius, + per_keypoint_offset=kp_config.per_keypoint_offset) + + +def object_detection_proto_to_params(od_config): + """Converts CenterNet.ObjectDetection proto to parameter namedtuple.""" + loss = losses_pb2.Loss() + # Add dummy classification loss to avoid the loss_builder throwing error. + # TODO(yuhuic): update the loss builder to take the classification loss + # directly. + loss.classification_loss.weighted_sigmoid.CopyFrom( + losses_pb2.WeightedSigmoidClassificationLoss()) + loss.localization_loss.CopyFrom(od_config.localization_loss) + _, localization_loss, _, _, _, _, _ = (losses_builder.build(loss)) + return center_net_meta_arch.ObjectDetectionParams( + localization_loss=localization_loss, + scale_loss_weight=od_config.scale_loss_weight, + offset_loss_weight=od_config.offset_loss_weight, + task_loss_weight=od_config.task_loss_weight) + + +def object_center_proto_to_params(oc_config): + """Converts CenterNet.ObjectCenter proto to parameter namedtuple.""" + loss = losses_pb2.Loss() + # Add dummy localization loss to avoid the loss_builder throwing error. + # TODO(yuhuic): update the loss builder to take the localization loss + # directly. + loss.localization_loss.weighted_l2.CopyFrom( + losses_pb2.WeightedL2LocalizationLoss()) + loss.classification_loss.CopyFrom(oc_config.classification_loss) + classification_loss, _, _, _, _, _, _ = (losses_builder.build(loss)) + return center_net_meta_arch.ObjectCenterParams( + classification_loss=classification_loss, + object_center_loss_weight=oc_config.object_center_loss_weight, + heatmap_bias_init=oc_config.heatmap_bias_init, + min_box_overlap_iou=oc_config.min_box_overlap_iou, + max_box_predictions=oc_config.max_box_predictions, + use_labeled_classes=oc_config.use_labeled_classes) + + +def mask_proto_to_params(mask_config): + """Converts CenterNet.MaskEstimation proto to parameter namedtuple.""" + loss = losses_pb2.Loss() + # Add dummy localization loss to avoid the loss_builder throwing error. + loss.localization_loss.weighted_l2.CopyFrom( + losses_pb2.WeightedL2LocalizationLoss()) + loss.classification_loss.CopyFrom(mask_config.classification_loss) + classification_loss, _, _, _, _, _, _ = (losses_builder.build(loss)) + return center_net_meta_arch.MaskParams( + classification_loss=classification_loss, + task_loss_weight=mask_config.task_loss_weight, + mask_height=mask_config.mask_height, + mask_width=mask_config.mask_width, + score_threshold=mask_config.score_threshold, + heatmap_bias_init=mask_config.heatmap_bias_init) + + +def densepose_proto_to_params(densepose_config): + """Converts CenterNet.DensePoseEstimation proto to parameter namedtuple.""" + classification_loss, localization_loss, _, _, _, _, _ = ( + losses_builder.build(densepose_config.loss)) + return center_net_meta_arch.DensePoseParams( + class_id=densepose_config.class_id, + classification_loss=classification_loss, + localization_loss=localization_loss, + part_loss_weight=densepose_config.part_loss_weight, + coordinate_loss_weight=densepose_config.coordinate_loss_weight, + num_parts=densepose_config.num_parts, + task_loss_weight=densepose_config.task_loss_weight, + upsample_to_input_res=densepose_config.upsample_to_input_res, + heatmap_bias_init=densepose_config.heatmap_bias_init) + + +def tracking_proto_to_params(tracking_config): + """Converts CenterNet.TrackEstimation proto to parameter namedtuple.""" + loss = losses_pb2.Loss() + # Add dummy localization loss to avoid the loss_builder throwing error. + # TODO(yuhuic): update the loss builder to take the localization loss + # directly. + loss.localization_loss.weighted_l2.CopyFrom( + losses_pb2.WeightedL2LocalizationLoss()) + loss.classification_loss.CopyFrom(tracking_config.classification_loss) + classification_loss, _, _, _, _, _, _ = losses_builder.build(loss) + return center_net_meta_arch.TrackParams( + num_track_ids=tracking_config.num_track_ids, + reid_embed_size=tracking_config.reid_embed_size, + classification_loss=classification_loss, + num_fc_layers=tracking_config.num_fc_layers, + task_loss_weight=tracking_config.task_loss_weight) + + +def temporal_offset_proto_to_params(temporal_offset_config): + """Converts CenterNet.TemporalOffsetEstimation proto to param-tuple.""" + loss = losses_pb2.Loss() + # Add dummy classification loss to avoid the loss_builder throwing error. + # TODO(yuhuic): update the loss builder to take the classification loss + # directly. + loss.classification_loss.weighted_sigmoid.CopyFrom( + losses_pb2.WeightedSigmoidClassificationLoss()) + loss.localization_loss.CopyFrom(temporal_offset_config.localization_loss) + _, localization_loss, _, _, _, _, _ = losses_builder.build(loss) + return center_net_meta_arch.TemporalOffsetParams( + localization_loss=localization_loss, + task_loss_weight=temporal_offset_config.task_loss_weight) + + +def _build_center_net_model(center_net_config, is_training, add_summaries): + """Build a CenterNet detection model. + + Args: + center_net_config: A CenterNet proto object with model configuration. + is_training: True if this model is being built for training purposes. + add_summaries: Whether to add tf summaries in the model. + + Returns: + CenterNetMetaArch based on the config. + + """ + + image_resizer_fn = image_resizer_builder.build( + center_net_config.image_resizer) + _check_feature_extractor_exists(center_net_config.feature_extractor.type) + feature_extractor = _build_center_net_feature_extractor( + center_net_config.feature_extractor) + object_center_params = object_center_proto_to_params( + center_net_config.object_center_params) + + object_detection_params = None + if center_net_config.HasField('object_detection_task'): + object_detection_params = object_detection_proto_to_params( + center_net_config.object_detection_task) + + keypoint_params_dict = None + if center_net_config.keypoint_estimation_task: + label_map_proto = label_map_util.load_labelmap( + center_net_config.keypoint_label_map_path) + keypoint_map_dict = { + item.name: item for item in label_map_proto.item if item.keypoints + } + keypoint_params_dict = {} + keypoint_class_id_set = set() + all_keypoint_indices = [] + for task in center_net_config.keypoint_estimation_task: + kp_params = keypoint_proto_to_params(task, keypoint_map_dict) + keypoint_params_dict[task.task_name] = kp_params + all_keypoint_indices.extend(kp_params.keypoint_indices) + if kp_params.class_id in keypoint_class_id_set: + raise ValueError(('Multiple keypoint tasks map to the same class id is ' + 'not allowed: %d' % kp_params.class_id)) + else: + keypoint_class_id_set.add(kp_params.class_id) + if len(all_keypoint_indices) > len(set(all_keypoint_indices)): + raise ValueError('Some keypoint indices are used more than once.') + + mask_params = None + if center_net_config.HasField('mask_estimation_task'): + mask_params = mask_proto_to_params(center_net_config.mask_estimation_task) + + densepose_params = None + if center_net_config.HasField('densepose_estimation_task'): + densepose_params = densepose_proto_to_params( + center_net_config.densepose_estimation_task) + + track_params = None + if center_net_config.HasField('track_estimation_task'): + track_params = tracking_proto_to_params( + center_net_config.track_estimation_task) + + temporal_offset_params = None + if center_net_config.HasField('temporal_offset_task'): + temporal_offset_params = temporal_offset_proto_to_params( + center_net_config.temporal_offset_task) + + return center_net_meta_arch.CenterNetMetaArch( + is_training=is_training, + add_summaries=add_summaries, + num_classes=center_net_config.num_classes, + feature_extractor=feature_extractor, + image_resizer_fn=image_resizer_fn, + object_center_params=object_center_params, + object_detection_params=object_detection_params, + keypoint_params_dict=keypoint_params_dict, + mask_params=mask_params, + densepose_params=densepose_params, + track_params=track_params, + temporal_offset_params=temporal_offset_params, + use_depthwise=center_net_config.use_depthwise, + compute_heatmap_sparse=center_net_config.compute_heatmap_sparse) + + +def _build_center_net_feature_extractor( + feature_extractor_config): + """Build a CenterNet feature extractor from the given config.""" + + if feature_extractor_config.type not in CENTER_NET_EXTRACTOR_FUNCTION_MAP: + raise ValueError('\'{}\' is not a known CenterNet feature extractor type' + .format(feature_extractor_config.type)) + + return CENTER_NET_EXTRACTOR_FUNCTION_MAP[feature_extractor_config.type]( + channel_means=list(feature_extractor_config.channel_means), + channel_stds=list(feature_extractor_config.channel_stds), + bgr_ordering=feature_extractor_config.bgr_ordering + ) + + +META_ARCH_BUILDER_MAP = { + 'ssd': _build_ssd_model, + 'faster_rcnn': _build_faster_rcnn_model, + 'experimental_model': _build_experimental_model, + 'center_net': _build_center_net_model +} + + +def build(model_config, is_training, add_summaries=True): + """Builds a DetectionModel based on the model config. + + Args: + model_config: A model.proto object containing the config for the desired + DetectionModel. + is_training: True if this model is being built for training purposes. + add_summaries: Whether to add tensorflow summaries in the model graph. + Returns: + DetectionModel based on the config. + + Raises: + ValueError: On invalid meta architecture or model. + """ + if not isinstance(model_config, model_pb2.DetectionModel): + raise ValueError('model_config not of type model_pb2.DetectionModel.') + + meta_architecture = model_config.WhichOneof('model') + + if meta_architecture not in META_ARCH_BUILDER_MAP: + raise ValueError('Unknown meta architecture: {}'.format(meta_architecture)) + else: + build_func = META_ARCH_BUILDER_MAP[meta_architecture] + return build_func(getattr(model_config, meta_architecture), is_training, + add_summaries) diff --git a/workspace/virtuallab/object_detection/builders/model_builder.pyc b/workspace/virtuallab/object_detection/builders/model_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e992de3ff1ffee0ed8da62da7b4cc09cbc767326 GIT binary patch literal 33813 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 1.0: + raise ValueError('iou_threshold not in [0, 1.0].') + if nms_config.max_detections_per_class > nms_config.max_total_detections: + raise ValueError('max_detections_per_class should be no greater than ' + 'max_total_detections.') + if nms_config.soft_nms_sigma < 0.0: + raise ValueError('soft_nms_sigma should be non-negative.') + if nms_config.use_combined_nms and nms_config.use_class_agnostic_nms: + raise ValueError('combined_nms does not support class_agnostic_nms.') + non_max_suppressor_fn = functools.partial( + post_processing.batch_multiclass_non_max_suppression, + score_thresh=nms_config.score_threshold, + iou_thresh=nms_config.iou_threshold, + max_size_per_class=nms_config.max_detections_per_class, + max_total_size=nms_config.max_total_detections, + use_static_shapes=nms_config.use_static_shapes, + use_class_agnostic_nms=nms_config.use_class_agnostic_nms, + max_classes_per_detection=nms_config.max_classes_per_detection, + soft_nms_sigma=nms_config.soft_nms_sigma, + use_partitioned_nms=nms_config.use_partitioned_nms, + use_combined_nms=nms_config.use_combined_nms, + change_coordinate_frame=nms_config.change_coordinate_frame, + use_hard_nms=nms_config.use_hard_nms, + use_cpu_nms=nms_config.use_cpu_nms) + + return non_max_suppressor_fn + + +def _score_converter_fn_with_logit_scale(tf_score_converter_fn, logit_scale): + """Create a function to scale logits then apply a Tensorflow function.""" + def score_converter_fn(logits): + scaled_logits = tf.multiply(logits, 1.0 / logit_scale, name='scale_logits') + return tf_score_converter_fn(scaled_logits, name='convert_scores') + score_converter_fn.__name__ = '%s_with_logit_scale' % ( + tf_score_converter_fn.__name__) + return score_converter_fn + + +def _build_score_converter(score_converter_config, logit_scale): + """Builds score converter based on the config. + + Builds one of [tf.identity, tf.sigmoid, tf.softmax] score converters based on + the config. + + Args: + score_converter_config: post_processing_pb2.PostProcessing.score_converter. + logit_scale: temperature to use for SOFTMAX score_converter. + + Returns: + Callable score converter op. + + Raises: + ValueError: On unknown score converter. + """ + if score_converter_config == post_processing_pb2.PostProcessing.IDENTITY: + return _score_converter_fn_with_logit_scale(tf.identity, logit_scale) + if score_converter_config == post_processing_pb2.PostProcessing.SIGMOID: + return _score_converter_fn_with_logit_scale(tf.sigmoid, logit_scale) + if score_converter_config == post_processing_pb2.PostProcessing.SOFTMAX: + return _score_converter_fn_with_logit_scale(tf.nn.softmax, logit_scale) + raise ValueError('Unknown score converter.') + + +def _build_calibrated_score_converter(score_converter_fn, calibration_config): + """Wraps a score_converter_fn, adding a calibration step. + + Builds a score converter function with a calibration transformation according + to calibration_builder.py. The score conversion function may be applied before + or after the calibration transformation, depending on the calibration method. + If the method is temperature scaling, the score conversion is + after the calibration transformation. Otherwise, the score conversion is + before the calibration transformation. Calibration applies positive monotonic + transformations to inputs (i.e. score ordering is strictly preserved or + adjacent scores are mapped to the same score). When calibration is + class-agnostic, the highest-scoring class remains unchanged, unless two + adjacent scores are mapped to the same value and one class arbitrarily + selected to break the tie. In per-class calibration, it's possible (though + rare in practice) that the highest-scoring class will change, since positive + monotonicity is only required to hold within each class. + + Args: + score_converter_fn: callable that takes logit scores as input. + calibration_config: post_processing_pb2.PostProcessing.calibration_config. + + Returns: + Callable calibrated score coverter op. + """ + calibration_fn = calibration_builder.build(calibration_config) + def calibrated_score_converter_fn(logits): + if (calibration_config.WhichOneof('calibrator') == + 'temperature_scaling_calibration'): + calibrated_logits = calibration_fn(logits) + return score_converter_fn(calibrated_logits) + else: + converted_logits = score_converter_fn(logits) + return calibration_fn(converted_logits) + + calibrated_score_converter_fn.__name__ = ( + 'calibrate_with_%s' % calibration_config.WhichOneof('calibrator')) + return calibrated_score_converter_fn diff --git a/workspace/virtuallab/object_detection/builders/post_processing_builder.pyc b/workspace/virtuallab/object_detection/builders/post_processing_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54046288901ce3a8e643a15818b7591cb4607ca6 GIT binary patch literal 7600 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/post_processing_builder_test.py b/workspace/virtuallab/object_detection/builders/post_processing_builder_test.py new file mode 100644 index 0000000..b7383c9 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/post_processing_builder_test.py @@ -0,0 +1,185 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for post_processing_builder.""" + +import tensorflow.compat.v1 as tf +from google.protobuf import text_format +from object_detection.builders import post_processing_builder +from object_detection.protos import post_processing_pb2 +from object_detection.utils import test_case + + +class PostProcessingBuilderTest(test_case.TestCase): + + def test_build_non_max_suppressor_with_correct_parameters(self): + post_processing_text_proto = """ + batch_non_max_suppression { + score_threshold: 0.7 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + soft_nms_sigma: 0.4 + } + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + non_max_suppressor, _ = post_processing_builder.build( + post_processing_config) + self.assertEqual(non_max_suppressor.keywords['max_size_per_class'], 100) + self.assertEqual(non_max_suppressor.keywords['max_total_size'], 300) + self.assertAlmostEqual(non_max_suppressor.keywords['score_thresh'], 0.7) + self.assertAlmostEqual(non_max_suppressor.keywords['iou_thresh'], 0.6) + self.assertAlmostEqual(non_max_suppressor.keywords['soft_nms_sigma'], 0.4) + + def test_build_non_max_suppressor_with_correct_parameters_classagnostic_nms( + self): + post_processing_text_proto = """ + batch_non_max_suppression { + score_threshold: 0.7 + iou_threshold: 0.6 + max_detections_per_class: 10 + max_total_detections: 300 + use_class_agnostic_nms: True + max_classes_per_detection: 1 + } + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + non_max_suppressor, _ = post_processing_builder.build( + post_processing_config) + self.assertEqual(non_max_suppressor.keywords['max_size_per_class'], 10) + self.assertEqual(non_max_suppressor.keywords['max_total_size'], 300) + self.assertEqual(non_max_suppressor.keywords['max_classes_per_detection'], + 1) + self.assertEqual(non_max_suppressor.keywords['use_class_agnostic_nms'], + True) + self.assertAlmostEqual(non_max_suppressor.keywords['score_thresh'], 0.7) + self.assertAlmostEqual(non_max_suppressor.keywords['iou_thresh'], 0.6) + + def test_build_identity_score_converter(self): + post_processing_text_proto = """ + score_converter: IDENTITY + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, score_converter = post_processing_builder.build( + post_processing_config) + self.assertEqual(score_converter.__name__, 'identity_with_logit_scale') + def graph_fn(): + inputs = tf.constant([1, 1], tf.float32) + outputs = score_converter(inputs) + return outputs + converted_scores = self.execute_cpu(graph_fn, []) + self.assertAllClose(converted_scores, [1, 1]) + + def test_build_identity_score_converter_with_logit_scale(self): + post_processing_text_proto = """ + score_converter: IDENTITY + logit_scale: 2.0 + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, score_converter = post_processing_builder.build(post_processing_config) + self.assertEqual(score_converter.__name__, 'identity_with_logit_scale') + + def graph_fn(): + inputs = tf.constant([1, 1], tf.float32) + outputs = score_converter(inputs) + return outputs + converted_scores = self.execute_cpu(graph_fn, []) + self.assertAllClose(converted_scores, [.5, .5]) + + def test_build_sigmoid_score_converter(self): + post_processing_text_proto = """ + score_converter: SIGMOID + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, score_converter = post_processing_builder.build(post_processing_config) + self.assertEqual(score_converter.__name__, 'sigmoid_with_logit_scale') + + def test_build_softmax_score_converter(self): + post_processing_text_proto = """ + score_converter: SOFTMAX + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, score_converter = post_processing_builder.build(post_processing_config) + self.assertEqual(score_converter.__name__, 'softmax_with_logit_scale') + + def test_build_softmax_score_converter_with_temperature(self): + post_processing_text_proto = """ + score_converter: SOFTMAX + logit_scale: 2.0 + """ + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, score_converter = post_processing_builder.build(post_processing_config) + self.assertEqual(score_converter.__name__, 'softmax_with_logit_scale') + + def test_build_calibrator_with_nonempty_config(self): + """Test that identity function used when no calibration_config specified.""" + # Calibration config maps all scores to 0.5. + post_processing_text_proto = """ + score_converter: SOFTMAX + calibration_config { + function_approximation { + x_y_pairs { + x_y_pair { + x: 0.0 + y: 0.5 + } + x_y_pair { + x: 1.0 + y: 0.5 + }}}}""" + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, calibrated_score_conversion_fn = post_processing_builder.build( + post_processing_config) + self.assertEqual(calibrated_score_conversion_fn.__name__, + 'calibrate_with_function_approximation') + + def graph_fn(): + input_scores = tf.constant([1, 1], tf.float32) + outputs = calibrated_score_conversion_fn(input_scores) + return outputs + calibrated_scores = self.execute_cpu(graph_fn, []) + self.assertAllClose(calibrated_scores, [0.5, 0.5]) + + def test_build_temperature_scaling_calibrator(self): + post_processing_text_proto = """ + score_converter: SOFTMAX + calibration_config { + temperature_scaling_calibration { + scaler: 2.0 + }}""" + post_processing_config = post_processing_pb2.PostProcessing() + text_format.Merge(post_processing_text_proto, post_processing_config) + _, calibrated_score_conversion_fn = post_processing_builder.build( + post_processing_config) + self.assertEqual(calibrated_score_conversion_fn.__name__, + 'calibrate_with_temperature_scaling_calibration') + + def graph_fn(): + input_scores = tf.constant([1, 1], tf.float32) + outputs = calibrated_score_conversion_fn(input_scores) + return outputs + calibrated_scores = self.execute_cpu(graph_fn, []) + self.assertAllClose(calibrated_scores, [0.5, 0.5]) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/preprocessor_builder.py b/workspace/virtuallab/object_detection/builders/preprocessor_builder.py new file mode 100644 index 0000000..b61239d --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/preprocessor_builder.py @@ -0,0 +1,428 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Builder for preprocessing steps.""" + +import tensorflow.compat.v1 as tf + +from object_detection.core import preprocessor +from object_detection.protos import preprocessor_pb2 + + +def _get_step_config_from_proto(preprocessor_step_config, step_name): + """Returns the value of a field named step_name from proto. + + Args: + preprocessor_step_config: A preprocessor_pb2.PreprocessingStep object. + step_name: Name of the field to get value from. + + Returns: + result_dict: a sub proto message from preprocessor_step_config which will be + later converted to a dictionary. + + Raises: + ValueError: If field does not exist in proto. + """ + for field, value in preprocessor_step_config.ListFields(): + if field.name == step_name: + return value + + raise ValueError('Could not get field %s from proto!' % step_name) + + +def _get_dict_from_proto(config): + """Helper function to put all proto fields into a dictionary. + + For many preprocessing steps, there's an trivial 1-1 mapping from proto fields + to function arguments. This function automatically populates a dictionary with + the arguments from the proto. + + Protos that CANNOT be trivially populated include: + * nested messages. + * steps that check if an optional field is set (ie. where None != 0). + * protos that don't map 1-1 to arguments (ie. list should be reshaped). + * fields requiring additional validation (ie. repeated field has n elements). + + Args: + config: A protobuf object that does not violate the conditions above. + + Returns: + result_dict: |config| converted into a python dictionary. + """ + result_dict = {} + for field, value in config.ListFields(): + result_dict[field.name] = value + return result_dict + + +# A map from a PreprocessingStep proto config field name to the preprocessing +# function that should be used. The PreprocessingStep proto should be parsable +# with _get_dict_from_proto. +PREPROCESSING_FUNCTION_MAP = { + 'normalize_image': + preprocessor.normalize_image, + 'random_pixel_value_scale': + preprocessor.random_pixel_value_scale, + 'random_image_scale': + preprocessor.random_image_scale, + 'random_rgb_to_gray': + preprocessor.random_rgb_to_gray, + 'random_adjust_brightness': + preprocessor.random_adjust_brightness, + 'random_adjust_contrast': + preprocessor.random_adjust_contrast, + 'random_adjust_hue': + preprocessor.random_adjust_hue, + 'random_adjust_saturation': + preprocessor.random_adjust_saturation, + 'random_distort_color': + preprocessor.random_distort_color, + 'random_jitter_boxes': + preprocessor.random_jitter_boxes, + 'random_crop_to_aspect_ratio': + preprocessor.random_crop_to_aspect_ratio, + 'random_black_patches': + preprocessor.random_black_patches, + 'random_jpeg_quality': + preprocessor.random_jpeg_quality, + 'random_downscale_to_target_pixels': + preprocessor.random_downscale_to_target_pixels, + 'random_patch_gaussian': + preprocessor.random_patch_gaussian, + 'rgb_to_gray': + preprocessor.rgb_to_gray, + 'scale_boxes_to_pixel_coordinates': ( + preprocessor.scale_boxes_to_pixel_coordinates), + 'subtract_channel_mean': + preprocessor.subtract_channel_mean, + 'convert_class_logits_to_softmax': + preprocessor.convert_class_logits_to_softmax, +} + + +# A map to convert from preprocessor_pb2.ResizeImage.Method enum to +# tf.image.ResizeMethod. +RESIZE_METHOD_MAP = { + preprocessor_pb2.ResizeImage.AREA: tf.image.ResizeMethod.AREA, + preprocessor_pb2.ResizeImage.BICUBIC: tf.image.ResizeMethod.BICUBIC, + preprocessor_pb2.ResizeImage.BILINEAR: tf.image.ResizeMethod.BILINEAR, + preprocessor_pb2.ResizeImage.NEAREST_NEIGHBOR: ( + tf.image.ResizeMethod.NEAREST_NEIGHBOR), +} + + +def build(preprocessor_step_config): + """Builds preprocessing step based on the configuration. + + Args: + preprocessor_step_config: PreprocessingStep configuration proto. + + Returns: + function, argmap: A callable function and an argument map to call function + with. + + Raises: + ValueError: On invalid configuration. + """ + step_type = preprocessor_step_config.WhichOneof('preprocessing_step') + + if step_type in PREPROCESSING_FUNCTION_MAP: + preprocessing_function = PREPROCESSING_FUNCTION_MAP[step_type] + step_config = _get_step_config_from_proto(preprocessor_step_config, + step_type) + function_args = _get_dict_from_proto(step_config) + return (preprocessing_function, function_args) + + if step_type == 'random_horizontal_flip': + config = preprocessor_step_config.random_horizontal_flip + return (preprocessor.random_horizontal_flip, + { + 'keypoint_flip_permutation': tuple( + config.keypoint_flip_permutation) or None, + 'probability': config.probability or None, + }) + + if step_type == 'random_vertical_flip': + config = preprocessor_step_config.random_vertical_flip + return (preprocessor.random_vertical_flip, + { + 'keypoint_flip_permutation': tuple( + config.keypoint_flip_permutation) or None, + 'probability': config.probability or None, + }) + + if step_type == 'random_rotation90': + config = preprocessor_step_config.random_rotation90 + return (preprocessor.random_rotation90, + { + 'keypoint_rot_permutation': tuple( + config.keypoint_rot_permutation) or None, + 'probability': config.probability or None, + }) + + if step_type == 'random_crop_image': + config = preprocessor_step_config.random_crop_image + return (preprocessor.random_crop_image, + { + 'min_object_covered': config.min_object_covered, + 'aspect_ratio_range': (config.min_aspect_ratio, + config.max_aspect_ratio), + 'area_range': (config.min_area, config.max_area), + 'overlap_thresh': config.overlap_thresh, + 'clip_boxes': config.clip_boxes, + 'random_coef': config.random_coef, + }) + + if step_type == 'random_pad_image': + config = preprocessor_step_config.random_pad_image + min_image_size = None + if (config.HasField('min_image_height') != + config.HasField('min_image_width')): + raise ValueError('min_image_height and min_image_width should be either ' + 'both set or both unset.') + if config.HasField('min_image_height'): + min_image_size = (config.min_image_height, config.min_image_width) + + max_image_size = None + if (config.HasField('max_image_height') != + config.HasField('max_image_width')): + raise ValueError('max_image_height and max_image_width should be either ' + 'both set or both unset.') + if config.HasField('max_image_height'): + max_image_size = (config.max_image_height, config.max_image_width) + + pad_color = config.pad_color or None + if pad_color: + if len(pad_color) != 3: + tf.logging.warn('pad_color should have 3 elements (RGB) if set!') + + pad_color = tf.cast([x for x in config.pad_color], dtype=tf.float32) + return (preprocessor.random_pad_image, + { + 'min_image_size': min_image_size, + 'max_image_size': max_image_size, + 'pad_color': pad_color, + }) + + if step_type == 'random_absolute_pad_image': + config = preprocessor_step_config.random_absolute_pad_image + + max_height_padding = config.max_height_padding or 1 + max_width_padding = config.max_width_padding or 1 + + pad_color = config.pad_color or None + if pad_color: + if len(pad_color) != 3: + tf.logging.warn('pad_color should have 3 elements (RGB) if set!') + + pad_color = tf.cast([x for x in config.pad_color], dtype=tf.float32) + + return (preprocessor.random_absolute_pad_image, + { + 'max_height_padding': max_height_padding, + 'max_width_padding': max_width_padding, + 'pad_color': pad_color, + }) + if step_type == 'random_crop_pad_image': + config = preprocessor_step_config.random_crop_pad_image + min_padded_size_ratio = config.min_padded_size_ratio + if min_padded_size_ratio and len(min_padded_size_ratio) != 2: + raise ValueError('min_padded_size_ratio should have 2 elements if set!') + max_padded_size_ratio = config.max_padded_size_ratio + if max_padded_size_ratio and len(max_padded_size_ratio) != 2: + raise ValueError('max_padded_size_ratio should have 2 elements if set!') + pad_color = config.pad_color or None + if pad_color: + if len(pad_color) != 3: + tf.logging.warn('pad_color should have 3 elements (RGB) if set!') + + pad_color = tf.cast([x for x in config.pad_color], dtype=tf.float32) + + kwargs = { + 'min_object_covered': config.min_object_covered, + 'aspect_ratio_range': (config.min_aspect_ratio, + config.max_aspect_ratio), + 'area_range': (config.min_area, config.max_area), + 'overlap_thresh': config.overlap_thresh, + 'clip_boxes': config.clip_boxes, + 'random_coef': config.random_coef, + 'pad_color': pad_color, + } + if min_padded_size_ratio: + kwargs['min_padded_size_ratio'] = tuple(min_padded_size_ratio) + if max_padded_size_ratio: + kwargs['max_padded_size_ratio'] = tuple(max_padded_size_ratio) + return (preprocessor.random_crop_pad_image, kwargs) + + if step_type == 'random_resize_method': + config = preprocessor_step_config.random_resize_method + return (preprocessor.random_resize_method, + { + 'target_size': [config.target_height, config.target_width], + }) + + if step_type == 'resize_image': + config = preprocessor_step_config.resize_image + method = RESIZE_METHOD_MAP[config.method] + return (preprocessor.resize_image, + { + 'new_height': config.new_height, + 'new_width': config.new_width, + 'method': method + }) + + if step_type == 'random_self_concat_image': + config = preprocessor_step_config.random_self_concat_image + return (preprocessor.random_self_concat_image, { + 'concat_vertical_probability': config.concat_vertical_probability, + 'concat_horizontal_probability': config.concat_horizontal_probability + }) + + if step_type == 'ssd_random_crop': + config = preprocessor_step_config.ssd_random_crop + if config.operations: + min_object_covered = [op.min_object_covered for op in config.operations] + aspect_ratio_range = [(op.min_aspect_ratio, op.max_aspect_ratio) + for op in config.operations] + area_range = [(op.min_area, op.max_area) for op in config.operations] + overlap_thresh = [op.overlap_thresh for op in config.operations] + clip_boxes = [op.clip_boxes for op in config.operations] + random_coef = [op.random_coef for op in config.operations] + return (preprocessor.ssd_random_crop, + { + 'min_object_covered': min_object_covered, + 'aspect_ratio_range': aspect_ratio_range, + 'area_range': area_range, + 'overlap_thresh': overlap_thresh, + 'clip_boxes': clip_boxes, + 'random_coef': random_coef, + }) + return (preprocessor.ssd_random_crop, {}) + + if step_type == 'autoaugment_image': + config = preprocessor_step_config.autoaugment_image + return (preprocessor.autoaugment_image, { + 'policy_name': config.policy_name, + }) + + if step_type == 'drop_label_probabilistically': + config = preprocessor_step_config.drop_label_probabilistically + return (preprocessor.drop_label_probabilistically, { + 'dropped_label': config.label, + 'drop_probability': config.drop_probability, + }) + + if step_type == 'remap_labels': + config = preprocessor_step_config.remap_labels + return (preprocessor.remap_labels, { + 'original_labels': config.original_labels, + 'new_label': config.new_label + }) + + if step_type == 'ssd_random_crop_pad': + config = preprocessor_step_config.ssd_random_crop_pad + if config.operations: + min_object_covered = [op.min_object_covered for op in config.operations] + aspect_ratio_range = [(op.min_aspect_ratio, op.max_aspect_ratio) + for op in config.operations] + area_range = [(op.min_area, op.max_area) for op in config.operations] + overlap_thresh = [op.overlap_thresh for op in config.operations] + clip_boxes = [op.clip_boxes for op in config.operations] + random_coef = [op.random_coef for op in config.operations] + min_padded_size_ratio = [tuple(op.min_padded_size_ratio) + for op in config.operations] + max_padded_size_ratio = [tuple(op.max_padded_size_ratio) + for op in config.operations] + pad_color = [(op.pad_color_r, op.pad_color_g, op.pad_color_b) + for op in config.operations] + return (preprocessor.ssd_random_crop_pad, + { + 'min_object_covered': min_object_covered, + 'aspect_ratio_range': aspect_ratio_range, + 'area_range': area_range, + 'overlap_thresh': overlap_thresh, + 'clip_boxes': clip_boxes, + 'random_coef': random_coef, + 'min_padded_size_ratio': min_padded_size_ratio, + 'max_padded_size_ratio': max_padded_size_ratio, + 'pad_color': pad_color, + }) + return (preprocessor.ssd_random_crop_pad, {}) + + if step_type == 'ssd_random_crop_fixed_aspect_ratio': + config = preprocessor_step_config.ssd_random_crop_fixed_aspect_ratio + if config.operations: + min_object_covered = [op.min_object_covered for op in config.operations] + area_range = [(op.min_area, op.max_area) for op in config.operations] + overlap_thresh = [op.overlap_thresh for op in config.operations] + clip_boxes = [op.clip_boxes for op in config.operations] + random_coef = [op.random_coef for op in config.operations] + return (preprocessor.ssd_random_crop_fixed_aspect_ratio, + { + 'min_object_covered': min_object_covered, + 'aspect_ratio': config.aspect_ratio, + 'area_range': area_range, + 'overlap_thresh': overlap_thresh, + 'clip_boxes': clip_boxes, + 'random_coef': random_coef, + }) + return (preprocessor.ssd_random_crop_fixed_aspect_ratio, {}) + + if step_type == 'ssd_random_crop_pad_fixed_aspect_ratio': + config = preprocessor_step_config.ssd_random_crop_pad_fixed_aspect_ratio + kwargs = {} + aspect_ratio = config.aspect_ratio + if aspect_ratio: + kwargs['aspect_ratio'] = aspect_ratio + min_padded_size_ratio = config.min_padded_size_ratio + if min_padded_size_ratio: + if len(min_padded_size_ratio) != 2: + raise ValueError('min_padded_size_ratio should have 2 elements if set!') + kwargs['min_padded_size_ratio'] = tuple(min_padded_size_ratio) + max_padded_size_ratio = config.max_padded_size_ratio + if max_padded_size_ratio: + if len(max_padded_size_ratio) != 2: + raise ValueError('max_padded_size_ratio should have 2 elements if set!') + kwargs['max_padded_size_ratio'] = tuple(max_padded_size_ratio) + if config.operations: + kwargs['min_object_covered'] = [op.min_object_covered + for op in config.operations] + kwargs['aspect_ratio_range'] = [(op.min_aspect_ratio, op.max_aspect_ratio) + for op in config.operations] + kwargs['area_range'] = [(op.min_area, op.max_area) + for op in config.operations] + kwargs['overlap_thresh'] = [op.overlap_thresh for op in config.operations] + kwargs['clip_boxes'] = [op.clip_boxes for op in config.operations] + kwargs['random_coef'] = [op.random_coef for op in config.operations] + return (preprocessor.ssd_random_crop_pad_fixed_aspect_ratio, kwargs) + + if step_type == 'random_square_crop_by_scale': + config = preprocessor_step_config.random_square_crop_by_scale + return preprocessor.random_square_crop_by_scale, { + 'scale_min': config.scale_min, + 'scale_max': config.scale_max, + 'max_border': config.max_border, + 'num_scales': config.num_scales + } + + if step_type == 'random_scale_crop_and_pad_to_square': + config = preprocessor_step_config.random_scale_crop_and_pad_to_square + return preprocessor.random_scale_crop_and_pad_to_square, { + 'scale_min': config.scale_min, + 'scale_max': config.scale_max, + 'output_size': config.output_size, + } + + raise ValueError('Unknown preprocessing step.') diff --git a/workspace/virtuallab/object_detection/builders/preprocessor_builder.pyc b/workspace/virtuallab/object_detection/builders/preprocessor_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7bb8dcc3bef014d8820c16855a382d55511e9a4 GIT binary patch literal 9897 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/preprocessor_builder_test.py b/workspace/virtuallab/object_detection/builders/preprocessor_builder_test.py new file mode 100644 index 0000000..9e90344 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/preprocessor_builder_test.py @@ -0,0 +1,758 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for preprocessor_builder.""" + +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format + +from object_detection.builders import preprocessor_builder +from object_detection.core import preprocessor +from object_detection.protos import preprocessor_pb2 + + +class PreprocessorBuilderTest(tf.test.TestCase): + + def assert_dictionary_close(self, dict1, dict2): + """Helper to check if two dicts with floatst or integers are close.""" + self.assertEqual(sorted(dict1.keys()), sorted(dict2.keys())) + for key in dict1: + value = dict1[key] + if isinstance(value, float): + self.assertAlmostEqual(value, dict2[key]) + else: + self.assertEqual(value, dict2[key]) + + def test_build_normalize_image(self): + preprocessor_text_proto = """ + normalize_image { + original_minval: 0.0 + original_maxval: 255.0 + target_minval: -1.0 + target_maxval: 1.0 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.normalize_image) + self.assertEqual(args, { + 'original_minval': 0.0, + 'original_maxval': 255.0, + 'target_minval': -1.0, + 'target_maxval': 1.0, + }) + + def test_build_random_horizontal_flip(self): + preprocessor_text_proto = """ + random_horizontal_flip { + keypoint_flip_permutation: 1 + keypoint_flip_permutation: 0 + keypoint_flip_permutation: 2 + keypoint_flip_permutation: 3 + keypoint_flip_permutation: 5 + keypoint_flip_permutation: 4 + probability: 0.5 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_horizontal_flip) + self.assertEqual(args, {'keypoint_flip_permutation': (1, 0, 2, 3, 5, 4), + 'probability': 0.5}) + + def test_build_random_vertical_flip(self): + preprocessor_text_proto = """ + random_vertical_flip { + keypoint_flip_permutation: 1 + keypoint_flip_permutation: 0 + keypoint_flip_permutation: 2 + keypoint_flip_permutation: 3 + keypoint_flip_permutation: 5 + keypoint_flip_permutation: 4 + probability: 0.5 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_vertical_flip) + self.assertEqual(args, {'keypoint_flip_permutation': (1, 0, 2, 3, 5, 4), + 'probability': 0.5}) + + def test_build_random_rotation90(self): + preprocessor_text_proto = """ + random_rotation90 { + keypoint_rot_permutation: 3 + keypoint_rot_permutation: 0 + keypoint_rot_permutation: 1 + keypoint_rot_permutation: 2 + probability: 0.5 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_rotation90) + self.assertEqual(args, {'keypoint_rot_permutation': (3, 0, 1, 2), + 'probability': 0.5}) + + def test_build_random_pixel_value_scale(self): + preprocessor_text_proto = """ + random_pixel_value_scale { + minval: 0.8 + maxval: 1.2 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_pixel_value_scale) + self.assert_dictionary_close(args, {'minval': 0.8, 'maxval': 1.2}) + + def test_build_random_image_scale(self): + preprocessor_text_proto = """ + random_image_scale { + min_scale_ratio: 0.8 + max_scale_ratio: 2.2 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_image_scale) + self.assert_dictionary_close(args, {'min_scale_ratio': 0.8, + 'max_scale_ratio': 2.2}) + + def test_build_random_rgb_to_gray(self): + preprocessor_text_proto = """ + random_rgb_to_gray { + probability: 0.8 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_rgb_to_gray) + self.assert_dictionary_close(args, {'probability': 0.8}) + + def test_build_random_adjust_brightness(self): + preprocessor_text_proto = """ + random_adjust_brightness { + max_delta: 0.2 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_adjust_brightness) + self.assert_dictionary_close(args, {'max_delta': 0.2}) + + def test_build_random_adjust_contrast(self): + preprocessor_text_proto = """ + random_adjust_contrast { + min_delta: 0.7 + max_delta: 1.1 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_adjust_contrast) + self.assert_dictionary_close(args, {'min_delta': 0.7, 'max_delta': 1.1}) + + def test_build_random_adjust_hue(self): + preprocessor_text_proto = """ + random_adjust_hue { + max_delta: 0.01 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_adjust_hue) + self.assert_dictionary_close(args, {'max_delta': 0.01}) + + def test_build_random_adjust_saturation(self): + preprocessor_text_proto = """ + random_adjust_saturation { + min_delta: 0.75 + max_delta: 1.15 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_adjust_saturation) + self.assert_dictionary_close(args, {'min_delta': 0.75, 'max_delta': 1.15}) + + def test_build_random_distort_color(self): + preprocessor_text_proto = """ + random_distort_color { + color_ordering: 1 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_distort_color) + self.assertEqual(args, {'color_ordering': 1}) + + def test_build_random_jitter_boxes(self): + preprocessor_text_proto = """ + random_jitter_boxes { + ratio: 0.1 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_jitter_boxes) + self.assert_dictionary_close(args, {'ratio': 0.1}) + + def test_build_random_crop_image(self): + preprocessor_text_proto = """ + random_crop_image { + min_object_covered: 0.75 + min_aspect_ratio: 0.75 + max_aspect_ratio: 1.5 + min_area: 0.25 + max_area: 0.875 + overlap_thresh: 0.5 + clip_boxes: False + random_coef: 0.125 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_crop_image) + self.assertEqual(args, { + 'min_object_covered': 0.75, + 'aspect_ratio_range': (0.75, 1.5), + 'area_range': (0.25, 0.875), + 'overlap_thresh': 0.5, + 'clip_boxes': False, + 'random_coef': 0.125, + }) + + def test_build_random_pad_image(self): + preprocessor_text_proto = """ + random_pad_image { + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_pad_image) + self.assertEqual(args, { + 'min_image_size': None, + 'max_image_size': None, + 'pad_color': None, + }) + + def test_build_random_absolute_pad_image(self): + preprocessor_text_proto = """ + random_absolute_pad_image { + max_height_padding: 50 + max_width_padding: 100 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_absolute_pad_image) + self.assertEqual(args, { + 'max_height_padding': 50, + 'max_width_padding': 100, + 'pad_color': None, + }) + + def test_build_random_crop_pad_image(self): + preprocessor_text_proto = """ + random_crop_pad_image { + min_object_covered: 0.75 + min_aspect_ratio: 0.75 + max_aspect_ratio: 1.5 + min_area: 0.25 + max_area: 0.875 + overlap_thresh: 0.5 + clip_boxes: False + random_coef: 0.125 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_crop_pad_image) + self.assertEqual(args, { + 'min_object_covered': 0.75, + 'aspect_ratio_range': (0.75, 1.5), + 'area_range': (0.25, 0.875), + 'overlap_thresh': 0.5, + 'clip_boxes': False, + 'random_coef': 0.125, + 'pad_color': None, + }) + + def test_build_random_crop_pad_image_with_optional_parameters(self): + preprocessor_text_proto = """ + random_crop_pad_image { + min_object_covered: 0.75 + min_aspect_ratio: 0.75 + max_aspect_ratio: 1.5 + min_area: 0.25 + max_area: 0.875 + overlap_thresh: 0.5 + clip_boxes: False + random_coef: 0.125 + min_padded_size_ratio: 0.5 + min_padded_size_ratio: 0.75 + max_padded_size_ratio: 0.5 + max_padded_size_ratio: 0.75 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_crop_pad_image) + self.assertEqual(args, { + 'min_object_covered': 0.75, + 'aspect_ratio_range': (0.75, 1.5), + 'area_range': (0.25, 0.875), + 'overlap_thresh': 0.5, + 'clip_boxes': False, + 'random_coef': 0.125, + 'min_padded_size_ratio': (0.5, 0.75), + 'max_padded_size_ratio': (0.5, 0.75), + 'pad_color': None, + }) + + def test_build_random_crop_to_aspect_ratio(self): + preprocessor_text_proto = """ + random_crop_to_aspect_ratio { + aspect_ratio: 0.85 + overlap_thresh: 0.35 + clip_boxes: False + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_crop_to_aspect_ratio) + self.assert_dictionary_close(args, {'aspect_ratio': 0.85, + 'overlap_thresh': 0.35, + 'clip_boxes': False}) + + def test_build_random_black_patches(self): + preprocessor_text_proto = """ + random_black_patches { + max_black_patches: 20 + probability: 0.95 + size_to_image_ratio: 0.12 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_black_patches) + self.assert_dictionary_close(args, {'max_black_patches': 20, + 'probability': 0.95, + 'size_to_image_ratio': 0.12}) + + def test_build_random_jpeg_quality(self): + preprocessor_text_proto = """ + random_jpeg_quality { + random_coef: 0.5 + min_jpeg_quality: 40 + max_jpeg_quality: 90 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Parse(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_jpeg_quality) + self.assert_dictionary_close(args, {'random_coef': 0.5, + 'min_jpeg_quality': 40, + 'max_jpeg_quality': 90}) + + def test_build_random_downscale_to_target_pixels(self): + preprocessor_text_proto = """ + random_downscale_to_target_pixels { + random_coef: 0.5 + min_target_pixels: 200 + max_target_pixels: 900 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Parse(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_downscale_to_target_pixels) + self.assert_dictionary_close(args, { + 'random_coef': 0.5, + 'min_target_pixels': 200, + 'max_target_pixels': 900 + }) + + def test_build_random_patch_gaussian(self): + preprocessor_text_proto = """ + random_patch_gaussian { + random_coef: 0.5 + min_patch_size: 10 + max_patch_size: 300 + min_gaussian_stddev: 0.2 + max_gaussian_stddev: 1.5 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Parse(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_patch_gaussian) + self.assert_dictionary_close(args, { + 'random_coef': 0.5, + 'min_patch_size': 10, + 'max_patch_size': 300, + 'min_gaussian_stddev': 0.2, + 'max_gaussian_stddev': 1.5 + }) + + def test_auto_augment_image(self): + preprocessor_text_proto = """ + autoaugment_image { + policy_name: 'v0' + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.autoaugment_image) + self.assert_dictionary_close(args, {'policy_name': 'v0'}) + + def test_drop_label_probabilistically(self): + preprocessor_text_proto = """ + drop_label_probabilistically{ + label: 2 + drop_probability: 0.5 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.drop_label_probabilistically) + self.assert_dictionary_close(args, { + 'dropped_label': 2, + 'drop_probability': 0.5 + }) + + def test_remap_labels(self): + preprocessor_text_proto = """ + remap_labels{ + original_labels: 1 + original_labels: 2 + new_label: 3 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.remap_labels) + self.assert_dictionary_close(args, { + 'original_labels': [1, 2], + 'new_label': 3 + }) + + def test_build_random_resize_method(self): + preprocessor_text_proto = """ + random_resize_method { + target_height: 75 + target_width: 100 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_resize_method) + self.assert_dictionary_close(args, {'target_size': [75, 100]}) + + def test_build_scale_boxes_to_pixel_coordinates(self): + preprocessor_text_proto = """ + scale_boxes_to_pixel_coordinates {} + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.scale_boxes_to_pixel_coordinates) + self.assertEqual(args, {}) + + def test_build_resize_image(self): + preprocessor_text_proto = """ + resize_image { + new_height: 75 + new_width: 100 + method: BICUBIC + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.resize_image) + self.assertEqual(args, {'new_height': 75, + 'new_width': 100, + 'method': tf.image.ResizeMethod.BICUBIC}) + + def test_build_rgb_to_gray(self): + preprocessor_text_proto = """ + rgb_to_gray {} + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.rgb_to_gray) + self.assertEqual(args, {}) + + def test_build_subtract_channel_mean(self): + preprocessor_text_proto = """ + subtract_channel_mean { + means: [1.0, 2.0, 3.0] + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.subtract_channel_mean) + self.assertEqual(args, {'means': [1.0, 2.0, 3.0]}) + + def test_random_self_concat_image(self): + preprocessor_text_proto = """ + random_self_concat_image { + concat_vertical_probability: 0.5 + concat_horizontal_probability: 0.25 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_self_concat_image) + self.assertEqual(args, {'concat_vertical_probability': 0.5, + 'concat_horizontal_probability': 0.25}) + + def test_build_ssd_random_crop(self): + preprocessor_text_proto = """ + ssd_random_crop { + operations { + min_object_covered: 0.0 + min_aspect_ratio: 0.875 + max_aspect_ratio: 1.125 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.0 + clip_boxes: False + random_coef: 0.375 + } + operations { + min_object_covered: 0.25 + min_aspect_ratio: 0.75 + max_aspect_ratio: 1.5 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.25 + clip_boxes: True + random_coef: 0.375 + } + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.ssd_random_crop) + self.assertEqual(args, {'min_object_covered': [0.0, 0.25], + 'aspect_ratio_range': [(0.875, 1.125), (0.75, 1.5)], + 'area_range': [(0.5, 1.0), (0.5, 1.0)], + 'overlap_thresh': [0.0, 0.25], + 'clip_boxes': [False, True], + 'random_coef': [0.375, 0.375]}) + + def test_build_ssd_random_crop_empty_operations(self): + preprocessor_text_proto = """ + ssd_random_crop { + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.ssd_random_crop) + self.assertEqual(args, {}) + + def test_build_ssd_random_crop_pad(self): + preprocessor_text_proto = """ + ssd_random_crop_pad { + operations { + min_object_covered: 0.0 + min_aspect_ratio: 0.875 + max_aspect_ratio: 1.125 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.0 + clip_boxes: False + random_coef: 0.375 + min_padded_size_ratio: [1.0, 1.0] + max_padded_size_ratio: [2.0, 2.0] + pad_color_r: 0.5 + pad_color_g: 0.5 + pad_color_b: 0.5 + } + operations { + min_object_covered: 0.25 + min_aspect_ratio: 0.75 + max_aspect_ratio: 1.5 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.25 + clip_boxes: True + random_coef: 0.375 + min_padded_size_ratio: [1.0, 1.0] + max_padded_size_ratio: [2.0, 2.0] + pad_color_r: 0.5 + pad_color_g: 0.5 + pad_color_b: 0.5 + } + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.ssd_random_crop_pad) + self.assertEqual(args, {'min_object_covered': [0.0, 0.25], + 'aspect_ratio_range': [(0.875, 1.125), (0.75, 1.5)], + 'area_range': [(0.5, 1.0), (0.5, 1.0)], + 'overlap_thresh': [0.0, 0.25], + 'clip_boxes': [False, True], + 'random_coef': [0.375, 0.375], + 'min_padded_size_ratio': [(1.0, 1.0), (1.0, 1.0)], + 'max_padded_size_ratio': [(2.0, 2.0), (2.0, 2.0)], + 'pad_color': [(0.5, 0.5, 0.5), (0.5, 0.5, 0.5)]}) + + def test_build_ssd_random_crop_fixed_aspect_ratio(self): + preprocessor_text_proto = """ + ssd_random_crop_fixed_aspect_ratio { + operations { + min_object_covered: 0.0 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.0 + clip_boxes: False + random_coef: 0.375 + } + operations { + min_object_covered: 0.25 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.25 + clip_boxes: True + random_coef: 0.375 + } + aspect_ratio: 0.875 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.ssd_random_crop_fixed_aspect_ratio) + self.assertEqual(args, {'min_object_covered': [0.0, 0.25], + 'aspect_ratio': 0.875, + 'area_range': [(0.5, 1.0), (0.5, 1.0)], + 'overlap_thresh': [0.0, 0.25], + 'clip_boxes': [False, True], + 'random_coef': [0.375, 0.375]}) + + def test_build_ssd_random_crop_pad_fixed_aspect_ratio(self): + preprocessor_text_proto = """ + ssd_random_crop_pad_fixed_aspect_ratio { + operations { + min_object_covered: 0.0 + min_aspect_ratio: 0.875 + max_aspect_ratio: 1.125 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.0 + clip_boxes: False + random_coef: 0.375 + } + operations { + min_object_covered: 0.25 + min_aspect_ratio: 0.75 + max_aspect_ratio: 1.5 + min_area: 0.5 + max_area: 1.0 + overlap_thresh: 0.25 + clip_boxes: True + random_coef: 0.375 + } + aspect_ratio: 0.875 + min_padded_size_ratio: [1.0, 1.0] + max_padded_size_ratio: [2.0, 2.0] + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, + preprocessor.ssd_random_crop_pad_fixed_aspect_ratio) + self.assertEqual(args, {'min_object_covered': [0.0, 0.25], + 'aspect_ratio': 0.875, + 'aspect_ratio_range': [(0.875, 1.125), (0.75, 1.5)], + 'area_range': [(0.5, 1.0), (0.5, 1.0)], + 'overlap_thresh': [0.0, 0.25], + 'clip_boxes': [False, True], + 'random_coef': [0.375, 0.375], + 'min_padded_size_ratio': (1.0, 1.0), + 'max_padded_size_ratio': (2.0, 2.0)}) + + def test_build_normalize_image_convert_class_logits_to_softmax(self): + preprocessor_text_proto = """ + convert_class_logits_to_softmax { + temperature: 2 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.convert_class_logits_to_softmax) + self.assertEqual(args, {'temperature': 2}) + + def test_random_crop_by_scale(self): + preprocessor_text_proto = """ + random_square_crop_by_scale { + scale_min: 0.25 + scale_max: 2.0 + num_scales: 8 + } + """ + preprocessor_proto = preprocessor_pb2.PreprocessingStep() + text_format.Merge(preprocessor_text_proto, preprocessor_proto) + function, args = preprocessor_builder.build(preprocessor_proto) + self.assertEqual(function, preprocessor.random_square_crop_by_scale) + self.assertEqual(args, { + 'scale_min': 0.25, + 'scale_max': 2.0, + 'num_scales': 8, + 'max_border': 128 + }) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.py b/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.py new file mode 100644 index 0000000..8f35087 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.py @@ -0,0 +1,59 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Builder for region similarity calculators.""" + +from object_detection.core import region_similarity_calculator +from object_detection.protos import region_similarity_calculator_pb2 + + +def build(region_similarity_calculator_config): + """Builds region similarity calculator based on the configuration. + + Builds one of [IouSimilarity, IoaSimilarity, NegSqDistSimilarity] objects. See + core/region_similarity_calculator.proto for details. + + Args: + region_similarity_calculator_config: RegionSimilarityCalculator + configuration proto. + + Returns: + region_similarity_calculator: RegionSimilarityCalculator object. + + Raises: + ValueError: On unknown region similarity calculator. + """ + + if not isinstance( + region_similarity_calculator_config, + region_similarity_calculator_pb2.RegionSimilarityCalculator): + raise ValueError( + 'region_similarity_calculator_config not of type ' + 'region_similarity_calculator_pb2.RegionsSimilarityCalculator') + + similarity_calculator = region_similarity_calculator_config.WhichOneof( + 'region_similarity') + if similarity_calculator == 'iou_similarity': + return region_similarity_calculator.IouSimilarity() + if similarity_calculator == 'ioa_similarity': + return region_similarity_calculator.IoaSimilarity() + if similarity_calculator == 'neg_sq_dist_similarity': + return region_similarity_calculator.NegSqDistSimilarity() + if similarity_calculator == 'thresholded_iou_similarity': + return region_similarity_calculator.ThresholdedIouSimilarity( + region_similarity_calculator_config.thresholded_iou_similarity + .iou_threshold) + + raise ValueError('Unknown region similarity calculator.') diff --git a/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.pyc b/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd9a71bd8824bcf4662d1412f5ca43870ad520cd GIT binary patch literal 1782 zcmcIk&2AGh5T4yl(l)fH1@R-q!GMsW3T-5~RR{qEq+VJn;YTVUYqRSl-fryF_6k)h z7s?rlD>oj2x8Qwv02q5$*)4?73z0loe;&`wH$M)3thK*=`a)wUe?I;{#HH_{NC5Ux zdXV^#c`)%|-v`g`1IPlHG+@7h`v!bO#U><8=yzK7@+OMMInNTN$WSVx*oaF(G@tM+ zQrsMoILhKYi;Pq{{lh0*n+kkOC7)KEf{Q(o@!nw9omMp@3DDkaqx*%Kre2SUCR3kwutygI{*F3MR+n+R!anat6YElv}5 zcUYuI&&!e7nCH-(N0iIFmiiKk>d5^O~EAx*Fs>POe`$cz=!V~K;E(9_2BZ9IN) zmYnQ%R;{Hr3a+`(CK560x$(Cy{$6}{rK*D4z8dp*yeF6(_V6y4H58}%yhLiPZZ4wE zoNhN)5r1A0JY}Y5-!*5;(w?QEV_z6^3&j~KY%Zamo=sr=S;GV9SfMSA?pRJ(Hm-}!1^W%*a*D9TeT}_c`NwUuC120 z7VNsm+$^CkrHPCwbqszFCbX|Yk^L*E`p2;wPwfiayMPLgXC?j@<7ISpAM=-I?7=#I NYg@sVzwWIEzW^$@96|s9 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder_test.py b/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder_test.py new file mode 100644 index 0000000..da72e73 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/region_similarity_calculator_builder_test.py @@ -0,0 +1,67 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for region_similarity_calculator_builder.""" + +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format +from object_detection.builders import region_similarity_calculator_builder +from object_detection.core import region_similarity_calculator +from object_detection.protos import region_similarity_calculator_pb2 as sim_calc_pb2 + + +class RegionSimilarityCalculatorBuilderTest(tf.test.TestCase): + + def testBuildIoaSimilarityCalculator(self): + similarity_calc_text_proto = """ + ioa_similarity { + } + """ + similarity_calc_proto = sim_calc_pb2.RegionSimilarityCalculator() + text_format.Merge(similarity_calc_text_proto, similarity_calc_proto) + similarity_calc = region_similarity_calculator_builder.build( + similarity_calc_proto) + self.assertTrue(isinstance(similarity_calc, + region_similarity_calculator.IoaSimilarity)) + + def testBuildIouSimilarityCalculator(self): + similarity_calc_text_proto = """ + iou_similarity { + } + """ + similarity_calc_proto = sim_calc_pb2.RegionSimilarityCalculator() + text_format.Merge(similarity_calc_text_proto, similarity_calc_proto) + similarity_calc = region_similarity_calculator_builder.build( + similarity_calc_proto) + self.assertTrue(isinstance(similarity_calc, + region_similarity_calculator.IouSimilarity)) + + def testBuildNegSqDistSimilarityCalculator(self): + similarity_calc_text_proto = """ + neg_sq_dist_similarity { + } + """ + similarity_calc_proto = sim_calc_pb2.RegionSimilarityCalculator() + text_format.Merge(similarity_calc_text_proto, similarity_calc_proto) + similarity_calc = region_similarity_calculator_builder.build( + similarity_calc_proto) + self.assertTrue(isinstance(similarity_calc, + region_similarity_calculator. + NegSqDistSimilarity)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/builders/target_assigner_builder.py b/workspace/virtuallab/object_detection/builders/target_assigner_builder.py new file mode 100644 index 0000000..f6434f6 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/target_assigner_builder.py @@ -0,0 +1,40 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""A function to build an object detection box coder from configuration.""" +from object_detection.builders import box_coder_builder +from object_detection.builders import matcher_builder +from object_detection.builders import region_similarity_calculator_builder +from object_detection.core import target_assigner + + +def build(target_assigner_config): + """Builds a TargetAssigner object based on the config. + + Args: + target_assigner_config: A target_assigner proto message containing config + for the desired target assigner. + + Returns: + TargetAssigner object based on the config. + """ + matcher_instance = matcher_builder.build(target_assigner_config.matcher) + similarity_calc_instance = region_similarity_calculator_builder.build( + target_assigner_config.similarity_calculator) + box_coder = box_coder_builder.build(target_assigner_config.box_coder) + return target_assigner.TargetAssigner( + matcher=matcher_instance, + similarity_calc=similarity_calc_instance, + box_coder_instance=box_coder) diff --git a/workspace/virtuallab/object_detection/builders/target_assigner_builder_test.py b/workspace/virtuallab/object_detection/builders/target_assigner_builder_test.py new file mode 100644 index 0000000..2796002 --- /dev/null +++ b/workspace/virtuallab/object_detection/builders/target_assigner_builder_test.py @@ -0,0 +1,50 @@ +"""Tests for google3.third_party.tensorflow_models.object_detection.builders.target_assigner_builder.""" +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +import tensorflow.compat.v1 as tf + +from google.protobuf import text_format + + +from object_detection.builders import target_assigner_builder +from object_detection.core import target_assigner +from object_detection.protos import target_assigner_pb2 + + +class TargetAssignerBuilderTest(tf.test.TestCase): + + def test_build_a_target_assigner(self): + target_assigner_text_proto = """ + matcher { + argmax_matcher {matched_threshold: 0.5} + } + similarity_calculator { + iou_similarity {} + } + box_coder { + faster_rcnn_box_coder {} + } + """ + target_assigner_proto = target_assigner_pb2.TargetAssigner() + text_format.Merge(target_assigner_text_proto, target_assigner_proto) + target_assigner_instance = target_assigner_builder.build( + target_assigner_proto) + self.assertIsInstance(target_assigner_instance, + target_assigner.TargetAssigner) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/colab_tutorials/.ipynb_checkpoints/object_detection_tutorial-checkpoint.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/.ipynb_checkpoints/object_detection_tutorial-checkpoint.ipynb new file mode 100644 index 0000000..75a81a1 --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/.ipynb_checkpoints/object_detection_tutorial-checkpoint.ipynb @@ -0,0 +1,783 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "V8-yl-s-WKMG" + }, + "source": [ + "# Object Detection API Demo\n", + "\n", + "
\n", + " \n", + " Run in Google Colab\n", + " \n", + "\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3cIrseUv6WKz" + }, + "source": [ + "Welcome to the [Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection). This notebook will walk you step by step through the process of using a pre-trained model to detect objects in an image." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VrJaG0cYN9yh" + }, + "source": [ + "> **Important**: This tutorial is to help you through the first step towards using [Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection) to build models. If you just just need an off the shelf model that does the job, see the [TFHub object detection example](https://colab.sandbox.google.com/github/tensorflow/hub/blob/master/examples/colab/object_detection.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kFSqkTCdWKMI" + }, + "source": [ + "# Setup" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "awjrpqy-6MaQ" + }, + "source": [ + "Important: If you're running on a local machine, be sure to follow the [installation instructions](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md). This notebook includes only what's necessary to run in Colab." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "p3UGXxUii5Ym" + }, + "source": [ + "### Install" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hGL97-GXjSUw" + }, + "outputs": [], + "source": [ + "!pip install -U --pre tensorflow==\"2.*\"\n", + "!pip install tf_slim" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "n_ap_s9ajTHH" + }, + "source": [ + "Make sure you have `pycocotools` installed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Bg8ZyA47i3pY" + }, + "outputs": [], + "source": [ + "!pip install pycocotools" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-vsOL3QR6kqs" + }, + "source": [ + "Get `tensorflow/models` or `cd` to parent directory of the repository." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ykA0c-om51s1" + }, + "outputs": [], + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "O219m6yWAj9l" + }, + "source": [ + "Compile protobufs and install the object_detection package" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "PY41vdYYNlXc" + }, + "outputs": [], + "source": [ + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "s62yJyQUcYbp" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing /home/job/models/research\n", + "Collecting Pillow>=1.0 (from object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/12/ad/61f8dfba88c4e56196bf6d056cdbba64dc9c5dfdfbc97d02e6472feed913/Pillow-6.2.2-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting Matplotlib>=2.1 (from object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/9d/40/5ba7d4a3f80d39d409f21899972596bf62c8606f1406a825029649eaa439/matplotlib-2.2.5-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting Cython>=0.28.1 (from object-detection==0.1)\n", + " Downloading https://files.pythonhosted.org/packages/59/c1/0b69d125ab9819869cffff2f416158acf2684bdb4bf54eccf887717e2cbd/Cython-0.29.21-cp27-cp27mu-manylinux1_x86_64.whl (1.9MB)\n", + "Collecting cycler>=0.10 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/f7/d2/e07d3ebb2bd7af696440ce7e754c59dd546ffe1bbe732c8ab68b9c834e61/cycler-0.10.0-py2.py3-none-any.whl\n", + "Collecting numpy>=1.7.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/3a/5f/47e578b3ae79e2624e205445ab77a1848acdaa2929a00eeef6b16eaaeb20/numpy-1.16.6-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting backports.functools-lru-cache (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/da/d1/080d2bb13773803648281a49e3918f65b31b7beebf009887a529357fd44a/backports.functools_lru_cache-1.6.1-py2.py3-none-any.whl\n", + "Collecting subprocess32 (from Matplotlib>=2.1->object-detection==0.1)\n", + "Collecting kiwisolver>=1.0.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/3d/78/cb9248b2289ec31e301137cedbe4ca503a74ca87f88cdbfd2f8be52323bf/kiwisolver-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting pytz (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/4f/a4/879454d49688e2fad93e59d7d4efda580b783c745fd2ec2a3adf87b0808d/pytz-2020.1-py2.py3-none-any.whl\n", + "Collecting six>=1.10 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl\n", + "Collecting python-dateutil>=2.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl\n", + "Collecting pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl\n", + "Collecting setuptools (from kiwisolver>=1.0.1->Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/e1/b7/182161210a13158cd3ccc41ee19aadef54496b74f2817cc147006ec932b4/setuptools-44.1.1-py2.py3-none-any.whl\n", + "Installing collected packages: Pillow, six, cycler, numpy, backports.functools-lru-cache, subprocess32, setuptools, kiwisolver, pytz, python-dateutil, pyparsing, Matplotlib, Cython, object-detection\n", + " Running setup.py install for object-detection: started\n", + " Running setup.py install for object-detection: finished with status 'done'\n", + "Successfully installed Cython-0.29.21 Matplotlib-2.2.5 Pillow-6.2.2 backports.functools-lru-cache-1.6.1 cycler-0.10.0 kiwisolver-1.1.0 numpy-1.16.6 object-detection-0.1 pyparsing-2.4.7 python-dateutil-2.8.1 pytz-2020.1 setuptools-44.1.1 six-1.15.0 subprocess32-3.5.4\n" + ] + } + ], + "source": [ + "%%bash \n", + "cd models/research\n", + "pip install ." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LBdjK2G5ywuc" + }, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hV4P5gyTWKMI" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import os\n", + "import six.moves.urllib as urllib\n", + "import sys\n", + "import tarfile\n", + "import tensorflow as tf\n", + "import zipfile\n", + "\n", + "from collections import defaultdict\n", + "from io import StringIO\n", + "from matplotlib import pyplot as plt\n", + "from PIL import Image\n", + "from IPython.display import display" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r5FNuiRPWKMN" + }, + "source": [ + "Import the object detection module." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4-IMl4b6BdGO" + }, + "outputs": [], + "source": [ + "from object_detection.utils import ops as utils_ops\n", + "from object_detection.utils import label_map_util\n", + "from object_detection.utils import visualization_utils as vis_util" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RYPCiag2iz_q" + }, + "source": [ + "Patches:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "mF-YlMl8c_bM" + }, + "outputs": [], + "source": [ + "# patch tf1 into `utils.ops`\n", + "utils_ops.tf = tf.compat.v1\n", + "\n", + "# Patch the location of gfile\n", + "tf.gfile = tf.io.gfile" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cfn_tRFOWKMO" + }, + "source": [ + "# Model preparation " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "X_sEBLpVWKMQ" + }, + "source": [ + "## Variables\n", + "\n", + "Any model exported using the `export_inference_graph.py` tool can be loaded here simply by changing the path.\n", + "\n", + "By default we use an \"SSD with Mobilenet\" model here. See the [detection model zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md) for a list of other models that can be run out-of-the-box with varying speeds and accuracies." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7ai8pLZZWKMS" + }, + "source": [ + "## Loader" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zm8xp-0eoItE" + }, + "outputs": [], + "source": [ + "def load_model(model_name):\n", + " base_url = 'http://download.tensorflow.org/models/object_detection/'\n", + " model_file = model_name + '.tar.gz'\n", + " model_dir = tf.keras.utils.get_file(\n", + " fname=model_name, \n", + " origin=base_url + model_file,\n", + " untar=True)\n", + "\n", + " model_dir = pathlib.Path(model_dir)/\"saved_model\"\n", + "\n", + " model = tf.saved_model.load(str(model_dir))\n", + "\n", + " return model" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_1MVVTcLWKMW" + }, + "source": [ + "## Loading label map\n", + "Label maps map indices to category names, so that when our convolution network predicts `5`, we know that this corresponds to `airplane`. Here we use internal utility functions, but anything that returns a dictionary mapping integers to appropriate string labels would be fine" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hDbpHkiWWKMX" + }, + "outputs": [], + "source": [ + "# List of the strings that is used to add correct label for each box.\n", + "PATH_TO_LABELS = 'models/annotations/label_map.pbtxt'\n", + "category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS, use_display_name=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oVU3U_J6IJVb" + }, + "source": [ + "For the sake of simplicity we will test on 2 images:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jG-zn5ykWKMd" + }, + "outputs": [], + "source": [ + "# If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.\n", + "PATH_TO_TEST_IMAGES_DIR = pathlib.Path('models/research/object_detection/test_images')\n", + "TEST_IMAGE_PATHS = sorted(list(PATH_TO_TEST_IMAGES_DIR.glob(\"*.jpg\")))\n", + "TEST_IMAGE_PATHS" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "H0_1AGhrWKMc" + }, + "source": [ + "# Detection" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "f7aOtOlebK7h" + }, + "source": [ + "Load an object detection model:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "1XNT0wxybKR6" + }, + "outputs": [], + "source": [ + "model_name = 'ssd_mobilenet_v1_coco_2017_11_17'\n", + "detection_model = load_model(model_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yN1AYfAEJIGp" + }, + "source": [ + "Check the model's input signature, it expects a batch of 3-color images of type uint8:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CK4cnry6wsHY" + }, + "outputs": [], + "source": [ + "print(detection_model.signatures['serving_default'].inputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Q8u3BjpMJXZF" + }, + "source": [ + "And returns several outputs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oLSZpfaYwuSk" + }, + "outputs": [], + "source": [ + "detection_model.signatures['serving_default'].output_dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "FZyKUJeuxvpT" + }, + "outputs": [], + "source": [ + "detection_model.signatures['serving_default'].output_shapes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JP5qZ7sXJpwG" + }, + "source": [ + "Add a wrapper function to call the model, and cleanup the outputs:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ajmR_exWyN76" + }, + "outputs": [], + "source": [ + "def run_inference_for_single_image(model, image):\n", + " image = np.asarray(image)\n", + " # The input needs to be a tensor, convert it using `tf.convert_to_tensor`.\n", + " input_tensor = tf.convert_to_tensor(image)\n", + " # The model expects a batch of images, so add an axis with `tf.newaxis`.\n", + " input_tensor = input_tensor[tf.newaxis,...]\n", + "\n", + " # Run inference\n", + " model_fn = model.signatures['serving_default']\n", + " output_dict = model_fn(input_tensor)\n", + "\n", + " # All outputs are batches tensors.\n", + " # Convert to numpy arrays, and take index [0] to remove the batch dimension.\n", + " # We're only interested in the first num_detections.\n", + " num_detections = int(output_dict.pop('num_detections'))\n", + " output_dict = {key:value[0, :num_detections].numpy() \n", + " for key,value in output_dict.items()}\n", + " output_dict['num_detections'] = num_detections\n", + "\n", + " # detection_classes should be ints.\n", + " output_dict['detection_classes'] = output_dict['detection_classes'].astype(np.int64)\n", + " \n", + " # Handle models with masks:\n", + " if 'detection_masks' in output_dict:\n", + " # Reframe the the bbox mask to the image size.\n", + " detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks(\n", + " output_dict['detection_masks'], output_dict['detection_boxes'],\n", + " image.shape[0], image.shape[1]) \n", + " detection_masks_reframed = tf.cast(detection_masks_reframed > 0.5,\n", + " tf.uint8)\n", + " output_dict['detection_masks_reframed'] = detection_masks_reframed.numpy()\n", + " \n", + " return output_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "z1wq0LVyMRR_" + }, + "source": [ + "Run it on each test image and show the results:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "DWh_1zz6aqxs" + }, + "outputs": [], + "source": [ + "def show_inference(model, image_path):\n", + " # the array based representation of the image will be used later in order to prepare the\n", + " # result image with boxes and labels on it.\n", + " image_np = np.array(Image.open(image_path))\n", + " # Actual detection.\n", + " output_dict = run_inference_for_single_image(model, image_np)\n", + " # Visualization of the results of a detection.\n", + " vis_util.visualize_boxes_and_labels_on_image_array(\n", + " image_np,\n", + " output_dict['detection_boxes'],\n", + " output_dict['detection_classes'],\n", + " output_dict['detection_scores'],\n", + " category_index,\n", + " instance_masks=output_dict.get('detection_masks_reframed', None),\n", + " use_normalized_coordinates=True,\n", + " line_thickness=8)\n", + "\n", + " display(Image.fromarray(image_np))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "3a5wMHN8WKMh" + }, + "outputs": [], + "source": [ + "for image_path in TEST_IMAGE_PATHS:\n", + " show_inference(detection_model, image_path)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DsspMPX3Cssg" + }, + "source": [ + "## Instance Segmentation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CzkVv_n2MxKC" + }, + "outputs": [], + "source": [ + "model_name = \"mask_rcnn_inception_resnet_v2_atrous_coco_2018_01_28\"\n", + "masking_model = load_model(model_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0S7aZi8ZOhVV" + }, + "source": [ + "The instance segmentation model includes a `detection_masks` output:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "vQ2Sj2VIOZLA" + }, + "outputs": [], + "source": [ + "masking_model.output_shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "AS57rZlnNL7W" + }, + "outputs": [], + "source": [ + "for image_path in TEST_IMAGE_PATHS:\n", + " show_inference(masking_model, image_path)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "last_runtime": { + "build_target": "//learning/brain/python/client:colab_notebook", + "kind": "private" + }, + "name": "object_detection_tutorial.ipynb", + "private_outputs": true, + "provenance": [ + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/colab_tutorials/object_detection_tutorial.ipynb", + "timestamp": 1594335690840 + }, + { + "file_id": "1LNYL6Zsn9Xlil2CVNOTsgDZQSBKeOjCh", + "timestamp": 1566498233247 + }, + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1566488313397 + }, + { + "file_id": "/piper/depot/google3/third_party/py/tensorflow_docs/g3doc/en/r2/tutorials/generative/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1566145894046 + }, + { + "file_id": "1nBPoWynOV0auSIy40eQcBIk9C6YRSkI8", + "timestamp": 1566145841085 + }, + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1556295408037 + }, + { + "file_id": "1layerger-51XwWOwYMY_5zHaCavCeQkO", + "timestamp": 1556214267924 + }, + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1556207836484 + }, + { + "file_id": "1w6mqQiNV3liPIX70NOgitOlDF1_4sRMw", + "timestamp": 1556154824101 + }, + { + "file_id": "https://github.com/tensorflow/models/blob/master/research/object_detection/object_detection_tutorial.ipynb", + "timestamp": 1556150293326 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/workspace/virtuallab/object_detection/colab_tutorials/context_rcnn_tutorial.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/context_rcnn_tutorial.ipynb new file mode 100644 index 0000000..b735cfb --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/context_rcnn_tutorial.ipynb @@ -0,0 +1,1500 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "context_rcnn_tutorial.ipynb", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "jZc1kMel3sZP", + "colab_type": "text" + }, + "source": [ + "# Context R-CNN Demo\n", + "\n", + "
\n", + " \n", + " Run in Google Colab\n", + " \n", + "\n", + " \n", + " View source on GitHub\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XuHWvdag3_b9", + "colab_type": "text" + }, + "source": [ + " This notebook will walk you step by step through the process of using a pre-trained model to build up a contextual memory bank for a set of images, and then detect objects in those images+context using [Context R-CNN](https://arxiv.org/abs/1912.03538)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "u0e-OOtn4hQ8", + "colab_type": "text" + }, + "source": [ + "# Setup" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w-UrhxBw4iLA", + "colab_type": "text" + }, + "source": [ + "Important: If you're running on a local machine, be sure to follow the [installation instructions](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md). This notebook includes only what's necessary to run in Colab." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SAqMxS4V4lqS", + "colab_type": "text" + }, + "source": [ + "### Install" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "BPkovrxF4o8n", + "colab_type": "code", + "outputId": "e1b8debc-ab73-4b3e-9e44-c86446c7cda1", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 785 + } + }, + "source": [ + "!pip install -U --pre tensorflow==\"2.*\"\n", + "!pip install tf_slim" + ], + "execution_count": 1, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Requirement already up-to-date: tensorflow==2.* in /usr/local/lib/python3.6/dist-packages (2.2.0)\n", + "Requirement already satisfied, skipping upgrade: scipy==1.4.1; python_version >= \"3\" in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.4.1)\n", + "Requirement already satisfied, skipping upgrade: protobuf>=3.8.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (3.10.0)\n", + "Requirement already satisfied, skipping upgrade: h5py<2.11.0,>=2.10.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (2.10.0)\n", + "Requirement already satisfied, skipping upgrade: opt-einsum>=2.3.2 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (3.2.1)\n", + "Requirement already satisfied, skipping upgrade: numpy<2.0,>=1.16.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.18.5)\n", + "Requirement already satisfied, skipping upgrade: wheel>=0.26; python_version >= \"3\" in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (0.34.2)\n", + "Requirement already satisfied, skipping upgrade: absl-py>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (0.9.0)\n", + "Requirement already satisfied, skipping upgrade: tensorflow-estimator<2.3.0,>=2.2.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (2.2.0)\n", + "Requirement already satisfied, skipping upgrade: google-pasta>=0.1.8 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (0.2.0)\n", + "Requirement already satisfied, skipping upgrade: grpcio>=1.8.6 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.29.0)\n", + "Requirement already satisfied, skipping upgrade: tensorboard<2.3.0,>=2.2.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (2.2.2)\n", + "Requirement already satisfied, skipping upgrade: gast==0.3.3 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (0.3.3)\n", + "Requirement already satisfied, skipping upgrade: astunparse==1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.6.3)\n", + "Requirement already satisfied, skipping upgrade: keras-preprocessing>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.1.2)\n", + "Requirement already satisfied, skipping upgrade: termcolor>=1.1.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.1.0)\n", + "Requirement already satisfied, skipping upgrade: six>=1.12.0 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.12.0)\n", + "Requirement already satisfied, skipping upgrade: wrapt>=1.11.1 in /usr/local/lib/python3.6/dist-packages (from tensorflow==2.*) (1.12.1)\n", + "Requirement already satisfied, skipping upgrade: setuptools in /usr/local/lib/python3.6/dist-packages (from protobuf>=3.8.0->tensorflow==2.*) (47.1.1)\n", + "Requirement already satisfied, skipping upgrade: google-auth<2,>=1.6.3 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (1.7.2)\n", + "Requirement already satisfied, skipping upgrade: werkzeug>=0.11.15 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (1.0.1)\n", + "Requirement already satisfied, skipping upgrade: requests<3,>=2.21.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (2.23.0)\n", + "Requirement already satisfied, skipping upgrade: google-auth-oauthlib<0.5,>=0.4.1 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (0.4.1)\n", + "Requirement already satisfied, skipping upgrade: markdown>=2.6.8 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (3.2.2)\n", + "Requirement already satisfied, skipping upgrade: tensorboard-plugin-wit>=1.6.0 in /usr/local/lib/python3.6/dist-packages (from tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (1.6.0.post3)\n", + "Requirement already satisfied, skipping upgrade: cachetools<3.2,>=2.0.0 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (3.1.1)\n", + "Requirement already satisfied, skipping upgrade: rsa<4.1,>=3.1.4 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (4.0)\n", + "Requirement already satisfied, skipping upgrade: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.6/dist-packages (from google-auth<2,>=1.6.3->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (0.2.8)\n", + "Requirement already satisfied, skipping upgrade: idna<3,>=2.5 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (2.9)\n", + "Requirement already satisfied, skipping upgrade: chardet<4,>=3.0.2 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (3.0.4)\n", + "Requirement already satisfied, skipping upgrade: certifi>=2017.4.17 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (2020.4.5.1)\n", + "Requirement already satisfied, skipping upgrade: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.6/dist-packages (from requests<3,>=2.21.0->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (1.24.3)\n", + "Requirement already satisfied, skipping upgrade: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.6/dist-packages (from google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (1.3.0)\n", + "Requirement already satisfied, skipping upgrade: importlib-metadata; python_version < \"3.8\" in /usr/local/lib/python3.6/dist-packages (from markdown>=2.6.8->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (1.6.0)\n", + "Requirement already satisfied, skipping upgrade: pyasn1>=0.1.3 in /usr/local/lib/python3.6/dist-packages (from rsa<4.1,>=3.1.4->google-auth<2,>=1.6.3->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (0.4.8)\n", + "Requirement already satisfied, skipping upgrade: oauthlib>=3.0.0 in /usr/local/lib/python3.6/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<0.5,>=0.4.1->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (3.1.0)\n", + "Requirement already satisfied, skipping upgrade: zipp>=0.5 in /usr/local/lib/python3.6/dist-packages (from importlib-metadata; python_version < \"3.8\"->markdown>=2.6.8->tensorboard<2.3.0,>=2.2.0->tensorflow==2.*) (3.1.0)\n", + "Collecting tf_slim\n", + "\u001b[?25l Downloading https://files.pythonhosted.org/packages/02/97/b0f4a64df018ca018cc035d44f2ef08f91e2e8aa67271f6f19633a015ff7/tf_slim-1.1.0-py2.py3-none-any.whl (352kB)\n", + "\u001b[K |████████████████████████████████| 358kB 2.8MB/s \n", + "\u001b[?25hRequirement already satisfied: absl-py>=0.2.2 in /usr/local/lib/python3.6/dist-packages (from tf_slim) (0.9.0)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from absl-py>=0.2.2->tf_slim) (1.12.0)\n", + "Installing collected packages: tf-slim\n", + "Successfully installed tf-slim-1.1.0\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zpKF8a2x4tec", + "colab_type": "text" + }, + "source": [ + "Make sure you have `pycocotools` installed" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "owcrp0AW4uCg", + "colab_type": "code", + "outputId": "001148a8-b0a8-43a1-f6df-225d86d90b8f", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + } + }, + "source": [ + "!pip install pycocotools" + ], + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Requirement already satisfied: pycocotools in /usr/local/lib/python3.6/dist-packages (2.0.0)\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wHFSRVaO4wuq", + "colab_type": "text" + }, + "source": [ + "Get `tensorflow/models` or `cd` to parent directory of the repository." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "E0ZuGKoi4wTn", + "colab_type": "code", + "outputId": "2b5d93cb-3548-4347-9b76-ce12bea44a56", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 136 + } + }, + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ], + "execution_count": 3, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Cloning into 'models'...\n", + "remote: Enumerating objects: 2694, done.\u001b[K\n", + "remote: Counting objects: 100% (2694/2694), done.\u001b[K\n", + "remote: Compressing objects: 100% (2370/2370), done.\u001b[K\n", + "remote: Total 2694 (delta 520), reused 1332 (delta 290), pack-reused 0\u001b[K\n", + "Receiving objects: 100% (2694/2694), 34.10 MiB | 29.32 MiB/s, done.\n", + "Resolving deltas: 100% (520/520), done.\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "GkqRm-WY47MR", + "colab_type": "text" + }, + "source": [ + "Compile protobufs and install the object_detection package" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "62Dn1_YU45O2", + "colab_type": "code", + "outputId": "439166dd-6202-4ff9-897d-100a35ae5af5", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 54 + } + }, + "source": [ + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=." + ], + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "text": [ + "object_detection/protos/input_reader.proto: warning: Import object_detection/protos/image_resizer.proto but not used.\n" + ], + "name": "stderr" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "83kNiD-24-ZB", + "colab_type": "code", + "outputId": "aa148939-7dcc-4fbd-ea48-41236523712c", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 343 + } + }, + "source": [ + "%%bash \n", + "cd models/research\n", + "pip install ." + ], + "execution_count": 5, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Processing /content/models/research\n", + "Requirement already satisfied: Pillow>=1.0 in /usr/local/lib/python3.6/dist-packages (from object-detection==0.1) (7.0.0)\n", + "Requirement already satisfied: Matplotlib>=2.1 in /usr/local/lib/python3.6/dist-packages (from object-detection==0.1) (3.2.1)\n", + "Requirement already satisfied: Cython>=0.28.1 in /usr/local/lib/python3.6/dist-packages (from object-detection==0.1) (0.29.19)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from Matplotlib>=2.1->object-detection==0.1) (0.10.0)\n", + "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from Matplotlib>=2.1->object-detection==0.1) (2.4.7)\n", + "Requirement already satisfied: numpy>=1.11 in /usr/local/lib/python3.6/dist-packages (from Matplotlib>=2.1->object-detection==0.1) (1.18.5)\n", + "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from Matplotlib>=2.1->object-detection==0.1) (2.8.1)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from Matplotlib>=2.1->object-detection==0.1) (1.2.0)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from cycler>=0.10->Matplotlib>=2.1->object-detection==0.1) (1.12.0)\n", + "Building wheels for collected packages: object-detection\n", + " Building wheel for object-detection (setup.py): started\n", + " Building wheel for object-detection (setup.py): finished with status 'done'\n", + " Created wheel for object-detection: filename=object_detection-0.1-cp36-none-any.whl size=1141324 sha256=1dff68de415a4ccc3af0e20b8f409a73d147d79720a713dcdc30f9bc8d4ab3a2\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-rlyj8yrw/wheels/94/49/4b/39b051683087a22ef7e80ec52152a27249d1a644ccf4e442ea\n", + "Successfully built object-detection\n", + "Installing collected packages: object-detection\n", + "Successfully installed object-detection-0.1\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LBdjK2G5ywuc" + }, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "hV4P5gyTWKMI", + "colab": {} + }, + "source": [ + "import numpy as np\n", + "import os\n", + "import six\n", + "import six.moves.urllib as urllib\n", + "import sys\n", + "import tarfile\n", + "import tensorflow as tf\n", + "import zipfile\n", + "import pathlib\n", + "import json\n", + "import datetime\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from collections import defaultdict\n", + "from io import StringIO\n", + "from matplotlib import pyplot as plt\n", + "from PIL import Image\n", + "from IPython.display import display" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r5FNuiRPWKMN" + }, + "source": [ + "Import the object detection module." + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "4-IMl4b6BdGO", + "colab": {} + }, + "source": [ + "from object_detection.utils import ops as utils_ops\n", + "from object_detection.utils import label_map_util\n", + "from object_detection.utils import visualization_utils as vis_utils" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RYPCiag2iz_q" + }, + "source": [ + "Patches:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "mF-YlMl8c_bM", + "colab": {} + }, + "source": [ + "# patch tf1 into `utils.ops`\n", + "utils_ops.tf = tf.compat.v1\n", + "\n", + "# Patch the location of gfile\n", + "tf.gfile = tf.io.gfile" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cfn_tRFOWKMO" + }, + "source": [ + "# Model preparation " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7ai8pLZZWKMS" + }, + "source": [ + "## Loader" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "zm8xp-0eoItE", + "colab": {} + }, + "source": [ + "def load_model(model_name):\n", + " base_url = 'http://download.tensorflow.org/models/object_detection/'\n", + " model_file = model_name + '.tar.gz'\n", + " model_dir = tf.keras.utils.get_file(\n", + " fname=model_name,\n", + " origin=base_url + model_file,\n", + " untar=True)\n", + "\n", + " model_dir = pathlib.Path(model_dir)/\"saved_model\"\n", + " model = tf.saved_model.load(str(model_dir))\n", + " model = model.signatures['serving_default']\n", + "\n", + " return model" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_1MVVTcLWKMW" + }, + "source": [ + "## Loading label map\n", + "Label maps map indices to category names, so that when our convolution network predicts `5`, we know that this corresponds to `zebra`. Here we use internal utility functions, but anything that returns a dictionary mapping integers to appropriate string labels would be fine" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "hDbpHkiWWKMX", + "colab": {} + }, + "source": [ + "# List of the strings that is used to add correct label for each box.\n", + "PATH_TO_LABELS = 'models/research/object_detection/data/snapshot_serengeti_label_map.pbtxt'\n", + "category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS, use_display_name=False)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oVU3U_J6IJVb" + }, + "source": [ + "We will test on a context group of images from one month at one camera from the Snapshot Serengeti val split defined on [LILA.science](http://lila.science/datasets/snapshot-serengeti), which was not seen during model training:\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "jG-zn5ykWKMd", + "outputId": "c7bbbb2f-0f6e-4380-fd92-c88c088bd766", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 85 + } + }, + "source": [ + "# If you want to test the code with your images, just add path to the images to\n", + "# the TEST_IMAGE_PATHS.\n", + "PATH_TO_TEST_IMAGES_DIR = pathlib.Path('models/research/object_detection/test_images/snapshot_serengeti')\n", + "TEST_IMAGE_PATHS = sorted(list(PATH_TO_TEST_IMAGES_DIR.glob(\"*.jpeg\")))\n", + "TEST_IMAGE_PATHS" + ], + "execution_count": 11, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[PosixPath('models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0038.jpeg'),\n", + " PosixPath('models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0039.jpeg'),\n", + " PosixPath('models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0040.jpeg'),\n", + " PosixPath('models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0041.jpeg')]" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 11 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "oBcQzptnQ-x6", + "colab_type": "text" + }, + "source": [ + "Load the metadata for each image" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "ZLLINOHcQ-An", + "colab_type": "code", + "colab": {} + }, + "source": [ + "test_data_json = 'models/research/object_detection/test_images/snapshot_serengeti/context_rcnn_demo_metadata.json'\n", + "with open(test_data_json, 'r') as f:\n", + " test_metadata = json.load(f)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "BgGTPHhkOAel", + "colab_type": "code", + "outputId": "1421a32a-c208-498f-931f-1bfeb25d6488", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 105 + } + }, + "source": [ + "image_id_to_datetime = {im['id']:im['date_captured'] for im in test_metadata['images']}\n", + "image_path_to_id = {im['file_name']: im['id'] \n", + " for im in test_metadata['images']}\n", + "image_path_to_id" + ], + "execution_count": 13, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0038.jpeg': 'S1/E03/E03_R3/S1_E03_R3_PICT0038',\n", + " 'models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0039.jpeg': 'S1/E03/E03_R3/S1_E03_R3_PICT0039',\n", + " 'models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0040.jpeg': 'S1/E03/E03_R3/S1_E03_R3_PICT0040',\n", + " 'models/research/object_detection/test_images/snapshot_serengeti/S1_E03_R3_PICT0041.jpeg': 'S1/E03/E03_R3/S1_E03_R3_PICT0041'}" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 13 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "H0_1AGhrWKMc" + }, + "source": [ + "# Generate Context Features for each image" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "kt3_pPQOj7ii", + "colab_type": "code", + "outputId": "fc72e978-f576-43f4-bcf1-3eb49fef5726", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 88 + } + }, + "source": [ + "faster_rcnn_model_name = 'faster_rcnn_resnet101_snapshot_serengeti_2020_06_10'\n", + "faster_rcnn_model = load_model(faster_rcnn_model_name)" + ], + "execution_count": 14, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Downloading data from http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_snapshot_serengeti_2020_06_10.tar.gz\n", + "588832768/588829839 [==============================] - 3s 0us/step\n", + "INFO:tensorflow:Saver not created because there are no variables in the graph to restore\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "k6Clkv_mBo_U", + "colab_type": "text" + }, + "source": [ + "Check the model's input signature, it expects a batch of 3-color images of type uint8." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "H1qNlFESBsTR", + "colab_type": "code", + "outputId": "9b8b84e0-d7a8-4ec9-d6e0-22d574cb6209", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 34 + } + }, + "source": [ + "faster_rcnn_model.inputs" + ], + "execution_count": 15, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[]" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 15 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eYS8KpRCBtBH", + "colab_type": "text" + }, + "source": [ + "And it returns several outputs. Note this model has been exported with additional output 'detection_features' which will be used to build the contextual memory bank." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "5M-1yxgfkmQl", + "colab_type": "code", + "outputId": "1da98c3b-79c5-4d19-d64c-3e9dbadc97c0", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 153 + } + }, + "source": [ + "faster_rcnn_model.output_dtypes" + ], + "execution_count": 16, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'detection_boxes': tf.float32,\n", + " 'detection_classes': tf.float32,\n", + " 'detection_features': tf.float32,\n", + " 'detection_multiclass_scores': tf.float32,\n", + " 'detection_scores': tf.float32,\n", + " 'num_detections': tf.float32,\n", + " 'raw_detection_boxes': tf.float32,\n", + " 'raw_detection_scores': tf.float32}" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 16 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "zVjNFFNIDCst", + "colab_type": "code", + "outputId": "edb46db0-05fb-4952-bc88-db09d7811b01", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 153 + } + }, + "source": [ + "faster_rcnn_model.output_shapes" + ], + "execution_count": 17, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'detection_boxes': TensorShape([None, 300, 4]),\n", + " 'detection_classes': TensorShape([None, 300]),\n", + " 'detection_features': TensorShape([None, None, None, None, None]),\n", + " 'detection_multiclass_scores': TensorShape([None, 300, 49]),\n", + " 'detection_scores': TensorShape([None, 300]),\n", + " 'num_detections': TensorShape([None]),\n", + " 'raw_detection_boxes': TensorShape([None, 300, 4]),\n", + " 'raw_detection_scores': TensorShape([None, 300, 49])}" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JP5qZ7sXJpwG" + }, + "source": [ + "Add a wrapper function to call the model, and cleanup the outputs:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "ajmR_exWyN76", + "colab": {} + }, + "source": [ + "def run_inference_for_single_image(model, image):\n", + " '''Run single image through tensorflow object detection saved_model.\n", + "\n", + " This function runs a saved_model on a (single) provided image and returns\n", + " inference results in numpy arrays.\n", + "\n", + " Args:\n", + " model: tensorflow saved_model. This model can be obtained using \n", + " export_inference_graph.py.\n", + " image: uint8 numpy array with shape (img_height, img_width, 3)\n", + "\n", + " Returns:\n", + " output_dict: a dictionary holding the following entries:\n", + " `num_detections`: an integer\n", + " `detection_boxes`: a numpy (float32) array of shape [N, 4]\n", + " `detection_classes`: a numpy (uint8) array of shape [N]\n", + " `detection_scores`: a numpy (float32) array of shape [N]\n", + " `detection_features`: a numpy (float32) array of shape [N, 7, 7, 2048]\n", + " '''\n", + " image = np.asarray(image)\n", + " # The input needs to be a tensor, convert it using `tf.convert_to_tensor`.\n", + " input_tensor = tf.convert_to_tensor(image)\n", + " # The model expects a batch of images, so add an axis with `tf.newaxis`.\n", + " input_tensor = input_tensor[tf.newaxis,...]\n", + "\n", + " # Run inference\n", + " output_dict = model(input_tensor)\n", + " # All outputs are batches tensors.\n", + " # Convert to numpy arrays, and take index [0] to remove the batch dimension.\n", + " # We're only interested in the first num_detections.\n", + " num_dets = output_dict.pop('num_detections')\n", + " num_detections = int(num_dets)\n", + " for key,value in output_dict.items():\n", + " output_dict[key] = value[0, :num_detections].numpy() \n", + " output_dict['num_detections'] = num_detections\n", + "\n", + " # detection_classes should be ints.\n", + " output_dict['detection_classes'] = output_dict['detection_classes'].astype(\n", + " np.int64)\n", + " return output_dict" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "un5SXxIxMaaV", + "colab_type": "text" + }, + "source": [ + "Functions for embedding context features" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "qvtvAZFDMoTM", + "colab_type": "code", + "colab": {} + }, + "source": [ + "def embed_date_captured(date_captured):\n", + " \"\"\"Encodes the datetime of the image.\n", + "\n", + " Takes a datetime object and encodes it into a normalized embedding of shape \n", + " [5], using hard-coded normalization factors for year, month, day, hour,\n", + " minute.\n", + "\n", + " Args:\n", + " date_captured: A datetime object.\n", + "\n", + " Returns:\n", + " A numpy float32 embedding of shape [5].\n", + " \"\"\"\n", + " embedded_date_captured = []\n", + " month_max = 12.0\n", + " day_max = 31.0\n", + " hour_max = 24.0\n", + " minute_max = 60.0\n", + " min_year = 1990.0\n", + " max_year = 2030.0\n", + "\n", + " year = (date_captured.year-min_year)/float(max_year-min_year)\n", + " embedded_date_captured.append(year)\n", + "\n", + " month = (date_captured.month-1)/month_max\n", + " embedded_date_captured.append(month)\n", + "\n", + " day = (date_captured.day-1)/day_max\n", + " embedded_date_captured.append(day)\n", + "\n", + " hour = date_captured.hour/hour_max\n", + " embedded_date_captured.append(hour)\n", + "\n", + " minute = date_captured.minute/minute_max\n", + " embedded_date_captured.append(minute)\n", + "\n", + " return np.asarray(embedded_date_captured)" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "xN8k5daOOA7b", + "colab_type": "code", + "colab": {} + }, + "source": [ + "def embed_position_and_size(box):\n", + " \"\"\"Encodes the bounding box of the object of interest.\n", + "\n", + " Takes a bounding box and encodes it into a normalized embedding of shape \n", + " [4] - the center point (x,y) and width and height of the box.\n", + "\n", + " Args:\n", + " box: A bounding box, formatted as [ymin, xmin, ymax, xmax].\n", + "\n", + " Returns:\n", + " A numpy float32 embedding of shape [4].\n", + " \"\"\"\n", + " ymin = box[0]\n", + " xmin = box[1]\n", + " ymax = box[2]\n", + " xmax = box[3]\n", + " w = xmax - xmin\n", + " h = ymax - ymin\n", + " x = xmin + w / 2.0\n", + " y = ymin + h / 2.0\n", + " return np.asarray([x, y, w, h])" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "lJe2qy8HPc6Z", + "colab_type": "code", + "colab": {} + }, + "source": [ + "def get_context_feature_embedding(date_captured, detection_boxes,\n", + " detection_features, detection_scores):\n", + " \"\"\"Extracts representative feature embedding for a given input image.\n", + "\n", + " Takes outputs of a detection model and focuses on the highest-confidence\n", + " detected object. Starts with detection_features and uses average pooling to\n", + " remove the spatial dimensions, then appends an embedding of the box position\n", + " and size, and an embedding of the date and time the image was captured,\n", + " returning a one-dimensional representation of the object.\n", + "\n", + " Args:\n", + " date_captured: A datetime string of format '%Y-%m-%d %H:%M:%S'.\n", + " detection_features: A numpy (float32) array of shape [N, 7, 7, 2048].\n", + " detection_boxes: A numpy (float32) array of shape [N, 4].\n", + " detection_scores: A numpy (float32) array of shape [N].\n", + "\n", + " Returns:\n", + " A numpy float32 embedding of shape [2057].\n", + " \"\"\"\n", + " date_captured = datetime.datetime.strptime(date_captured,'%Y-%m-%d %H:%M:%S')\n", + " temporal_embedding = embed_date_captured(date_captured)\n", + " embedding = detection_features[0]\n", + " pooled_embedding = np.mean(np.mean(embedding, axis=1), axis=0)\n", + " box = detection_boxes[0]\n", + " position_embedding = embed_position_and_size(box)\n", + " bb_embedding = np.concatenate((pooled_embedding, position_embedding))\n", + " embedding = np.expand_dims(np.concatenate((bb_embedding,temporal_embedding)),\n", + " axis=0)\n", + " score = detection_scores[0]\n", + " return embedding, score" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "z1wq0LVyMRR_" + }, + "source": [ + "Run it on each test image and use the output detection features and metadata to build up a context feature bank:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "DWh_1zz6aqxs", + "colab": {} + }, + "source": [ + "def run_inference(model, image_path, date_captured, resize_image=True):\n", + " \"\"\"Runs inference over a single input image and extracts contextual features.\n", + "\n", + " Args:\n", + " model: A tensorflow saved_model object.\n", + " image_path: Absolute path to the input image.\n", + " date_captured: A datetime string of format '%Y-%m-%d %H:%M:%S'.\n", + " resize_image: Whether to resize the input image before running inference.\n", + "\n", + " Returns:\n", + " context_feature: A numpy float32 array of shape [2057].\n", + " score: A numpy float32 object score for the embedded object.\n", + " output_dict: The saved_model output dictionary for the image.\n", + " \"\"\"\n", + " with open(image_path,'rb') as f:\n", + " image = Image.open(f)\n", + " if resize_image:\n", + " image.thumbnail((640,640),Image.ANTIALIAS)\n", + " image_np = np.array(image)\n", + "\n", + " # Actual detection.\n", + " output_dict = run_inference_for_single_image(model, image_np)\n", + "\n", + " context_feature, score = get_context_feature_embedding(\n", + " date_captured, output_dict['detection_boxes'],\n", + " output_dict['detection_features'], output_dict['detection_scores'])\n", + " return context_feature, score, output_dict" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "3a5wMHN8WKMh", + "colab": {} + }, + "source": [ + "context_features = []\n", + "scores = []\n", + "faster_rcnn_results = {}\n", + "for image_path in TEST_IMAGE_PATHS:\n", + " image_id = image_path_to_id[str(image_path)]\n", + " date_captured = image_id_to_datetime[image_id]\n", + " context_feature, score, results = run_inference(\n", + " faster_rcnn_model, image_path, date_captured)\n", + " faster_rcnn_results[image_id] = results\n", + " context_features.append(context_feature)\n", + " scores.append(score)\n", + "\n", + "# Concatenate all extracted context embeddings into a contextual memory bank.\n", + "context_features_matrix = np.concatenate(context_features, axis=0)\n" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DsspMPX3Cssg" + }, + "source": [ + "## Run Detection With Context" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "f7aOtOlebK7h" + }, + "source": [ + "Load a context r-cnn object detection model:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "1XNT0wxybKR6", + "outputId": "cc5b0677-cf16-46c2-9ae5-32681725f856", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 88 + } + }, + "source": [ + "context_rcnn_model_name = 'context_rcnn_resnet101_snapshot_serengeti_2020_06_10'\n", + "context_rcnn_model = load_model(context_rcnn_model_name)\n" + ], + "execution_count": 24, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Downloading data from http://download.tensorflow.org/models/object_detection/context_rcnn_resnet101_snapshot_serengeti_2020_06_10.tar.gz\n", + "724664320/724658931 [==============================] - 3s 0us/step\n", + "INFO:tensorflow:Saver not created because there are no variables in the graph to restore\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "G6IGGtGqBH6y", + "colab_type": "text" + }, + "source": [ + "We need to define the expected context padding size for the\n", + "model, this must match the definition in the model config (max_num_context_features)." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "4oh9XNLBjkTL", + "colab_type": "code", + "colab": {} + }, + "source": [ + "context_padding_size = 2000" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yN1AYfAEJIGp" + }, + "source": [ + "Check the model's input signature, it expects a batch of 3-color images of type uint8, plus context_features padded to the maximum context feature size for this model (2000) and valid_context_size to represent the non-padded context features: " + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "CK4cnry6wsHY", + "outputId": "d77af014-769f-4e20-b4ac-bfdd40502128", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 68 + } + }, + "source": [ + "context_rcnn_model.inputs" + ], + "execution_count": 26, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[,\n", + " ,\n", + " ]" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 26 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Q8u3BjpMJXZF" + }, + "source": [ + "And returns several outputs:" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "oLSZpfaYwuSk", + "outputId": "63a3903f-529b-41f9-b742-9b81c4c5e096", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 136 + } + }, + "source": [ + "context_rcnn_model.output_dtypes" + ], + "execution_count": 27, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'detection_boxes': tf.float32,\n", + " 'detection_classes': tf.float32,\n", + " 'detection_multiclass_scores': tf.float32,\n", + " 'detection_scores': tf.float32,\n", + " 'num_detections': tf.float32,\n", + " 'raw_detection_boxes': tf.float32,\n", + " 'raw_detection_scores': tf.float32}" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 27 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "FZyKUJeuxvpT", + "outputId": "d2feeaba-2bb2-4779-a96a-94a8a0aff362", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 136 + } + }, + "source": [ + "context_rcnn_model.output_shapes" + ], + "execution_count": 28, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'detection_boxes': TensorShape([1, 300, 4]),\n", + " 'detection_classes': TensorShape([1, 300]),\n", + " 'detection_multiclass_scores': TensorShape([1, 300, 49]),\n", + " 'detection_scores': TensorShape([1, 300]),\n", + " 'num_detections': TensorShape([1]),\n", + " 'raw_detection_boxes': TensorShape([1, 300, 4]),\n", + " 'raw_detection_scores': TensorShape([1, 300, 49])}" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 28 + } + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "CzkVv_n2MxKC", + "colab": {} + }, + "source": [ + "def run_context_rcnn_inference_for_single_image(\n", + " model, image, context_features, context_padding_size):\n", + " '''Run single image through a Context R-CNN saved_model.\n", + "\n", + " This function runs a saved_model on a (single) provided image and provided \n", + " contextual features and returns inference results in numpy arrays.\n", + "\n", + " Args:\n", + " model: tensorflow Context R-CNN saved_model. This model can be obtained\n", + " using export_inference_graph.py and setting side_input fields. \n", + " Example export call - \n", + " python export_inference_graph.py \\\n", + " --input_type image_tensor \\\n", + " --pipeline_config_path /path/to/context_rcnn_model.config \\\n", + " --trained_checkpoint_prefix /path/to/context_rcnn_model.ckpt \\\n", + " --output_directory /path/to/output_dir \\\n", + " --use_side_inputs True \\\n", + " --side_input_shapes 1,2000,2057/1 \\\n", + " --side_input_names context_features,valid_context_size \\\n", + " --side_input_types float,int \\\n", + " --input_shape 1,-1,-1,3\n", + "\n", + " image: uint8 numpy array with shape (img_height, img_width, 3)\n", + " context_features: A numpy float32 contextual memory bank of shape \n", + " [num_context_examples, 2057]\n", + " context_padding_size: The amount of expected padding in the contextual\n", + " memory bank, defined in the Context R-CNN config as \n", + " max_num_context_features.\n", + "\n", + " Returns:\n", + " output_dict: a dictionary holding the following entries:\n", + " `num_detections`: an integer\n", + " `detection_boxes`: a numpy (float32) array of shape [N, 4]\n", + " `detection_classes`: a numpy (uint8) array of shape [N]\n", + " `detection_scores`: a numpy (float32) array of shape [N]\n", + " '''\n", + " image = np.asarray(image)\n", + " # The input image needs to be a tensor, convert it using \n", + " # `tf.convert_to_tensor`.\n", + " image_tensor = tf.convert_to_tensor(\n", + " image, name='image_tensor')[tf.newaxis,...]\n", + "\n", + " context_features = np.asarray(context_features)\n", + " valid_context_size = context_features.shape[0]\n", + " valid_context_size_tensor = tf.convert_to_tensor(\n", + " valid_context_size, name='valid_context_size')[tf.newaxis,...]\n", + " padded_context_features = np.pad(\n", + " context_features,\n", + " ((0,context_padding_size-valid_context_size),(0,0)), mode='constant')\n", + " padded_context_features_tensor = tf.convert_to_tensor(\n", + " padded_context_features,\n", + " name='context_features',\n", + " dtype=tf.float32)[tf.newaxis,...]\n", + "\n", + " # Run inference\n", + " output_dict = model(\n", + " inputs=image_tensor,\n", + " context_features=padded_context_features_tensor,\n", + " valid_context_size=valid_context_size_tensor)\n", + " # All outputs are batches tensors.\n", + " # Convert to numpy arrays, and take index [0] to remove the batch dimension.\n", + " # We're only interested in the first num_detections.\n", + " num_dets = output_dict.pop('num_detections')\n", + " num_detections = int(num_dets)\n", + " for key,value in output_dict.items():\n", + " output_dict[key] = value[0, :num_detections].numpy() \n", + " output_dict['num_detections'] = num_detections\n", + "\n", + " # detection_classes should be ints.\n", + " output_dict['detection_classes'] = output_dict['detection_classes'].astype(np.int64)\n", + " return output_dict" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "0FqVkR3Agc6U", + "colab_type": "code", + "colab": {} + }, + "source": [ + "def show_context_rcnn_inference(\n", + " model, image_path, context_features, faster_rcnn_output_dict,\n", + " context_padding_size, resize_image=True):\n", + " \"\"\"Runs inference over a single input image and visualizes Faster R-CNN vs. \n", + " Context R-CNN results.\n", + "\n", + " Args:\n", + " model: A tensorflow saved_model object.\n", + " image_path: Absolute path to the input image.\n", + " context_features: A numpy float32 contextual memory bank of shape \n", + " [num_context_examples, 2057]\n", + " faster_rcnn_output_dict: The output_dict corresponding to this input image\n", + " from the single-frame Faster R-CNN model, which was previously used to\n", + " build the memory bank.\n", + " context_padding_size: The amount of expected padding in the contextual\n", + " memory bank, defined in the Context R-CNN config as \n", + " max_num_context_features.\n", + " resize_image: Whether to resize the input image before running inference.\n", + "\n", + " Returns:\n", + " context_rcnn_image_np: Numpy image array showing Context R-CNN Results.\n", + " faster_rcnn_image_np: Numpy image array showing Faster R-CNN Results.\n", + " \"\"\"\n", + "\n", + " # the array based representation of the image will be used later in order to prepare the\n", + " # result image with boxes and labels on it.\n", + " with open(image_path,'rb') as f:\n", + " image = Image.open(f)\n", + " if resize_image:\n", + " image.thumbnail((640,640),Image.ANTIALIAS)\n", + " image_np = np.array(image)\n", + " image.thumbnail((400,400),Image.ANTIALIAS)\n", + " context_rcnn_image_np = np.array(image)\n", + " \n", + " faster_rcnn_image_np = np.copy(context_rcnn_image_np)\n", + "\n", + " # Actual detection.\n", + " output_dict = run_context_rcnn_inference_for_single_image(\n", + " model, image_np, context_features, context_padding_size)\n", + "\n", + " # Visualization of the results of a context_rcnn detection.\n", + " vis_utils.visualize_boxes_and_labels_on_image_array(\n", + " context_rcnn_image_np,\n", + " output_dict['detection_boxes'],\n", + " output_dict['detection_classes'],\n", + " output_dict['detection_scores'],\n", + " category_index,\n", + " use_normalized_coordinates=True,\n", + " line_thickness=2)\n", + " \n", + " # Visualization of the results of a faster_rcnn detection.\n", + " vis_utils.visualize_boxes_and_labels_on_image_array(\n", + " faster_rcnn_image_np,\n", + " faster_rcnn_output_dict['detection_boxes'],\n", + " faster_rcnn_output_dict['detection_classes'],\n", + " faster_rcnn_output_dict['detection_scores'],\n", + " category_index,\n", + " use_normalized_coordinates=True,\n", + " line_thickness=2)\n", + " return context_rcnn_image_np, faster_rcnn_image_np" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3cYa2B8uAYx0", + "colab_type": "text" + }, + "source": [ + "Define Matplotlib parameters for pretty visualizations" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "9F8okR1uAQ0T", + "colab_type": "code", + "colab": {} + }, + "source": [ + "%matplotlib inline\n", + "plt.rcParams['axes.grid'] = False\n", + "plt.rcParams['xtick.labelsize'] = False\n", + "plt.rcParams['ytick.labelsize'] = False\n", + "plt.rcParams['xtick.top'] = False\n", + "plt.rcParams['xtick.bottom'] = False\n", + "plt.rcParams['ytick.left'] = False\n", + "plt.rcParams['ytick.right'] = False\n", + "plt.rcParams['figure.figsize'] = [15,10]" + ], + "execution_count": 0, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YGj7nXXQAaQ7", + "colab_type": "text" + }, + "source": [ + "Run Context R-CNN inference and compare results to Faster R-CNN" + ] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "vQ2Sj2VIOZLA", + "outputId": "1c043894-09e5-4c9f-a99d-ae21d6e72d0c", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + } + }, + "source": [ + "for image_path in TEST_IMAGE_PATHS:\n", + " image_id = image_path_to_id[str(image_path)]\n", + " faster_rcnn_output_dict = faster_rcnn_results[image_id]\n", + " context_rcnn_image, faster_rcnn_image = show_context_rcnn_inference(\n", + " context_rcnn_model, image_path, context_features_matrix,\n", + " faster_rcnn_output_dict, context_padding_size)\n", + " plt.subplot(1,2,1)\n", + " plt.imshow(faster_rcnn_image)\n", + " plt.title('Faster R-CNN')\n", + " plt.subplot(1,2,2)\n", + " plt.imshow(context_rcnn_image)\n", + " plt.title('Context R-CNN')\n", + " plt.show()" + ], + "execution_count": 32, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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4eFhnHLKKVixYgUefPBBVCoVnHDCCUilUnKwkbaKBx98EPfddx8uueQStLW14eabb8bChQtxxx13oFwuI51OY3h4GBaLBXV1dVIscGRkRGogeDweDA0NoVQqIRwOw2g0oqenBwaDAY2Njcjlcujv70elUkE4HIbD4UAul8PQ0BCcTqccjLNYLHjzzTdx5513wu12IxgM4pRTTpFDhCoqlQri8TgSiQScTifC4bAMKlZzDwaDsNvtiEajcqiQFdFDoZBkCtKEovFJhUoA27dvxxNPPIHu7m4sWLAAl112GcLhMF566SU8/vjjyGQyWLx4MS677DIEAoGqcc3PyeVyePPNN/Hkk09icHAQS5YswcaNGxGPx/H0009LQcJ8Po+lS5di/fr1ci1q8CyVSti9ezeee+45+d5TTz0VBoMBzz77LF544QWMjo7irLPOwvr162G321EoFDA4OIinn34aGzduRG1tLbLZLJ599lk8+eSTMBqNWL58OTZt2iT1dg5HIcvn83j++efxyCOPoFQqYcmSJfjCF74gdVR6enqwZ88etLW1Ye7cuQf9vFQqhUcffRS/+93vAABnnXUWLrrooqqaMk8++SRcLhfOO+88Ie2J18MEB3v27MHbb7+Nyy+/HEajEYODg3jmmWeqiOTkk0/G/PnzdQz6I0JzpOZIjWMHmiM1R04HM/rAxSwobW1tuPbaaxGPx9He3o5QKCTpb/m6XC6Hp59+GiaTCTfddBPq6urwwAMPYM+ePSiXy+jr68Mvf/lL7Ny5E263G+vXr8fatWuxa9cuPProo+jo6MCiRYtw2WWXYevWrdixYwe+/vWvw2az4fbbb0dbWxuuvPJKPPTQQ3jhhRdQLBZx2mmn4ZJLLsHAwAC+973voba2Ful0GuFwGLW1tfjf//1f7N+/HzabDXfeeSduuOEGrFq1SjLIEHv37sUvf/lLvPfee4hEIrjwwguxbNkyvPnmm3jwwQcRjUaxcuVKnHLKKXjqqaewf/9+GAwGBAIB9PX14ZxzzsEXv/hFKdimofFJRaVSQUdHB/7t3/4N+/btQ1NTE37605/C6/XizDPPxK233gqr1YqFCxfim9/8JrLZLK677rqq+hXAuIq1d+9efP/730dvby8aGhrws5/9DE6nE3PmzEFfXx8ymQx6enrw1FNP4dprr8X69evl/IqqYvX19eH222/Hjh07UFdXh//6r//CddddB5PJhDvvvBORSATpdBrf/e530draivnz5+O5557Dk08+iSeeeAJr1qxBKBTCtm3bcPPNNyMSiSAUCuEHP/gB/H4/zj///EMGWCr0O3fuxLXXXouGhgbMnj0b//Iv/wKPx4OLLroIL7/8Mv77v/8bu3fvxvXXX485c+YAADKZDHbt2oVQKITGxkaYzWY88cQTuPHGG3HOOecgk8ngpptuQktLC1atWoVcLofnn38et956K0477TSsWbMGDocDsVgM7e3tCIfDiEQiMBqN6O3txcsvv4x///d/h9FoxGWXXQaDwYC9e/filltuwYoVKxAIBGC1WlFTU4P58+cf2QGkUQXNkZojNY4taI48eLtojqzGjD5wcVuOhRHnz5+PHTt2SEE2vsZgGK9P0N3dLQUaAeCaa66Bz+dDoVDA3XffjZ/85Cc444wz8MYbb6CzsxMejwcPPPAAXnnlFTQ3N+Pee++V7cOHH34Yp59+OhwOB371q1/hBz/4AZ555hl84xvfgM/nAwBs3boVjY2NqK2txf333w+LxYITTzwRZ5xxhqR5JSGq26yqLzSXy+GOO+7A448/js9+9rN48cUX0dXVheuvvx4//OEP8c4776C1tRU///nPsXPnTrz++uuiNhaLRRQKBezYsQPr169HOByeyebX0JgxqPN1x44d2LVrFzZv3owLL7wQf/3Xf43f/va3aG1txb59+/CP//iP+MIXvoDnnnsO9957L6677roPBGIqgLt378bXv/51nHbaabjpppvwyiuv4KyzzsI//MM/wGKx4Kc//SleeuklnHnmmQf1Tw8ODuKNN97AFVdcgeXLl+Pv//7v8bvf/U5S1958880ol8v40pe+hBdffBGtra3o6urCtm3bJCVzsVjEI488gkwmgx/+8Icol8u46qqr8Mwzz2Dt2rWSAvhghMIdiAceeAADAwP4zW9+g+bmZrz99tt46KGHcO6556K3txfvvfeeFDbl5w0NDeFnP/sZTj31VFx88cUwGAz4xS9+gbq6Ovz4xz/G4OAgVq1ahcceewwrV65EV1cX7rnnHkSj0arCw+3t7fj5z3+OtWvX4txzz4XVakU6nUZHRwd27tyJJUuWyPWz5s2mTZsQDAYRiUQwb948ANpO+MeE5kjNkRrHBjRHao6cLg5eEvmjfOj/+1ZZ7I5koR7yY3594EBF++effx4vvfQSxsbGcN9992HJkiW46667cP3112P+/PnYuXMntm3bhrVr1+I73/kO5syZg6eeegrz5s3DrFmz8Nhjj+HXv/41amtrsWHDBvz2t79FLBZDQ0ODWBrefPNNGAwGuFwunH322bjjjjtw5ZVX4rLLLsNXvvIVzJ49GwsWLMC3vvUtLF26tEphIwk+9dRTWL16Ne666y781V/9FSKRCF599VW8+OKLOOuss/BP//RPmDt3Lnbs2IFMJoN169Zh0aJFOOGEE9Dc3Iyuri4p3Kah8UlGpVJBLBaD0WhEJBKB3+/HiSeeiPb2dimO+vvf/x733Xcfstks1q1bJwUb1Z9sNouhoSHYbDa0trairq4Ora2t6OjoQDQalZofTz31FBobG/Enf/InyOVyUliRP/l8HvX19bjhhhuwYcMG8X47nU7E43E0NjYiEAhgzpw5aGhowGuvvQaLxYLNmzfj9NNPl3oaVBP5Wp/Ph4ULF6KzsxPDw8Ny74fC7t27UVNTg9bWVthsNqxevRodHR0YHR3Fpk2bcO65537gjEogEMD69euxePFiqT2yb98+nHjiiWK/Wrx4MV5++WWk02k8/PDDeOedd9DS0iL2rkqlgkgkgvXr12PBggWicLa1teHqq69GU1OTFHCsVCrYu3cvkskktm7div/8z//Er3/9aylIqfHHh+ZIzZEaxwY0R04NzZEHMKM7XMA4YYyNjeHNN9/Eyy+/jGAwiPr6ehiNRqRSKfT29koxwXnz5uGdd97Be++9B6/Xi3vuuQfnnXeeFITj4LFarRgeHkZjY6M0GovrZbNZ1NXVYenSpXj44YeRSqXwuc99DsFgEMViEQBw5plnwmKx4Be/+AXi8TgqlfEK5aeeeiqWLVsmW5+sXM+B3d3djXA4jD179sDn88nTLgAZ5LFYDF1dXTIoWEiPT+pGoxHBYFAKz7Ea+sGyz2hofJIwUUEzm82or69HLBYTC9C2bduQSqWQTCaxatUqPPjgg9i+fTvsdrsEPofDgf7+fpjNZpTLZTidTgSDQYyMjCCTycBkMqGrqwvbt2/Hhg0bYLVa8a//+q9SuJRwuVy4/PLL8Wd/9md455138B//8R8IhUJYs2YNfvWrX0mBTZ/Ph2AwiO7ubpTLZSlmObGgLDBOGjabDTU1Ndi7dy9SqRTq6+sP2S4A5Nqo5jU1NcnZFZ6XUdV/APD7/bjgggvks0iIhMPhQH19PV5//XW8+uqrePTRR/H5z38e+/fvR6lUEgUvEomgoaFB3sdzNoxFKux2Oz772c9i7dq16OzsxJYtW7Bs2TJcfPHFVWnHNY48NEdqjtQ4dqA58uDtAmiOVDHjSTPy+TxeffVV9PT0IJFIYPPmzTAajchkMnjsscewY8cOAOPFwS688EK88MILuPXWW+FwOOB2u/Hnf/7nsFqt+OIXv4gtW7bga1/7GhKJBHw+H0455RS0t7fjySefxLZt29Dd3Y2LL74Ys2bNwjnnnIMHHngA2WwW5513HoxGI84880w8+OCDePHFF+F0OpHJZLB06Xi9znw+/4GAXl9fj9bWVrzyyiu49dZbsXz5clxwwQX45je/iTlz5uDuu++Gy+XC+vXr8fDDD+Oaa67B+++/j9bWVpx99tnYv38/nn32WezZswfxeByrV6/Ga6+9hnw+j7GxMVExWGlcQ+NogBqYisUient7EYlE8NZbb6G9vR3f+ta3MHv2bLS3t+Oee+7BX/zFX6C+vl4OswKAxWJBLBYDcEAFHx4eRjAYlMPxL7/8MlKpFM466yxYLBaEQiG4XK4PBFqTyYRdu3bhu9/9LoaHh/G3f/u3WLBggRAJAMTjcQwPD6OlpUUyH6n3wcQEJJWxsTEMDQ2hpqYGXq9XrvNQ4GeQqPbv349QKIRQKHTQ9xSLRYyNjcFsNgsxqNncaCWbO3cu7r//frS3t+OMM85AZ2cnCoUCnn/+eXzmM5+B3W5HPp8XAjkYSqUS5s+fjxtuuAEnn3wyXn31VWzZsgVDQ0OHvD+NmYXmSM2RGsceNEceHJojD2BGH7gCgQDWrVsHu92OhoYGnHzyyVi3bh22bduGNWvWiEWgXC6jVCphxYoVuP322/HII48gkUjg29/+Ns4++2yYTCb8zd/8DXw+H9566y0sWrQIGzZswLJlyxAMBlFbW4vOzk5ccMEF2LBhA2pqarBy5UpccsklyOfzOO2002AwGLB27Vrcdddd2Lp1KwqFAr7xjW9g7dq1SCQSuOKKK7Bo0aKqp/hQKIQvf/nLqKmpwcDAABYuXAiPx4NFixahubkZlUoFVqsVN910E5qbm7Fz506ce+65uPTSS/HpT38aNTU1eOyxx9DZ2YlLL70Uy5cvR2trK+bOnYv6+noUi0Wk02lEo1HJ8qKh8UmGwWBAQ0MDzGYz+vv7kU6n8e6776KpqQmZTAbxeByzZ8/G8uXLYbPZsH37dqxZswann3561eeUy2U888wzePbZZ7Fv3z7MmjULnZ2daG5uRjAYRDabxdatWxEOh3HiiSfCbrfj8ssvnzRddDKZxN13341UKoW/+7u/w4oVK1Aul9Hc3IxXX30Vw8PDGB4eRl9fHzZu3AiLxSJKOtV/k8mET3/603jjjTewb98+lEolvPvuu1i+fDlqamoOu32WLl2KrVu3oqOjA7NmzcJLL72E1tZWhMNhORStpr82GAzo6enBj370IyxfvhwbN26E3W7H4sWLsX37duRyOUSjUezYsQNf+cpX4PF4sHTpUnR2dqKvrw8GgwG7du3CihUrsHfvXtx3331Ys2aNEDDbSP3ecrmMu+66C4FAAD/60Y+QSCRQKBTk9Rp/PGiO1BypcWxBc+TU0Bx5ADPywMVgPH/+fHz/+9+HyWQSNc7hcGDVqlVYsGABSqWS+CWdTidCoRA2btyI5cuXo1AoIBwOS5BtbGzEV7/6VUkrGwwGYTKZsGTJErS2tiKbzcLn88HtdsNgMCAYDOKmm24CANTU1MBgMMDj8eCSSy7BGWecgVKphGAwCJfLBafTia997WsfqCdgNBqxYsUKtLW1IZ/Po6amBlarFTfeeKMcEDYYDJgzZw6uvvpqJBIJ2O12hEIhmEwmnHLKKZg7dy5GR0fh8/ngcDjQ0tICq9WKYrEoikGxWJQBqz3qGp9EqClmP/WpT2HRokXYsmULnnjiCbz11lu45ZZbMG/ePPzmN7/BjTfeiEWLFuGtt97C5ZdfDrvd/gELQKVSwZIlS7Bo0SL8+Mc/xkMPPYSenh6cf/75qKmpwa5du/CHP/wBp59+OmprayVGqGTCufPss8/ikUcewYIFC/D000/jf/7nf7B69WqcccYZ+P3vf4/bbrsNyWQSDodDFpaVSgXZbBaZTAbAuM1h06ZNuP/++/GXf/mXiEQiGBsbw9q1a+W8zFSWJs7bSy+9FPfeey+uvPJKzJ8/H52dnbj66qthsVhQLBblmtWij/Toh0IhObNz5ZVX4oorrsCXvvQlZLNZ2O12XHDBBZg9ezYuuugi9PX1IR6Pw2Aw4OKLL4bf70cikUBraysCgcAHCklmMhlRLc1mMxYvXow77rgDlUoFb7/9NiKRCJYtW1Z1LxpHDpojNUdqHFvQHKk5crow3XbbbR/pA7Zs2XLbVVddBYPBALPZDJ/PB6/XC5fLJU/NVqsVPp8Pfr9ffjwejzxher1eBAIBeT1/bDabfBY71mg0wm63w+PxfKDSvVpsjjCbzXC5XPD5fJJ212w2w263S1EzVcGjT93r9UrxN6fTKdmZCJvNBo/HA5fLJd5OZm7itZlMJthsNvHj22w2OBwOOJ1One5W46gAD8+3trbCYBivo7NhwwZs3LgRbW1tOOGEE5DL5TA2Nobzzz8fV199tRRDnfjjdrsxZ84c8XJv2rQJ5513HgKBAGKxGNLpNC699FLMnj27al6q87NcLqO9vR0mkwn19fXI5/PI5/NoaGjA6tWr0dLSIvWHNm/ejJUrV8JsNsNkMiGRSMDv92PdunUSExYuXIhEIgGv14vPf/7zOPfcc6vi0KHg9/uxZMkSDA0NwWQy4U//9E/xuc99TpSxdDoNl8uFFStWoLa2FgaDAU6nEwsXLkTr/x8irlQqaGpqQltbG3p7e+H1evHVr34Vn/nMZ+B0OuH3+2G329Hd3Y2Ghgacc845cDgc8Hg8OPHEE9HU1CR2CV7zwMAATjrpJKxevRoAsGjRIoRCIQwPD2PevHm45ppr8KlPfUrSeU8nFn3729/uu+2227ZMayAdx9AcqTlS49iF5sipcbxx5E9+8hNcddVV3550rBxOlpGpsHTp0sprr70GYPKMJeoT5eHiYO+Z6vcTv/9wP2NiIx7qWqd7bYf6LA2NTzLUYoHZbBb5fB52u10q3JdKJWQyGTmo6nQ6qxZ4Ez+Hh/iLxWLV5xSLRWQyGTidzqpt/MnmdjablYryBD+Ln18ul+F2u2E2m2URODo6irGxMfj9fsnCVC6XkUqlRC1UF6iH2zblchnJZBLlchkOh0PuCYAkNXA4HKKm8T0Tg3ihUEAqlRICJ6mx/VOpFADA6/VWWSEmEm6lUkEymZQdDO4a5PN5ZLNZWWBPXEgfLoxG4x8qlcrSab3pOIbmyKl/f6jP0tD4JENz5KHb5njiyGXLluH111+f9A0z+sCloaFx7EENmmpmMf5NPYirprWe7HPUn4mvnSwwHuozCNUuwEPIE4Mlr39ikciJ3zud4MrrOFgb8G8Tf6eC1z1Z26jv4fvUtp/4OQStaRPbV/2MD/Ow9f/v1Q9c04DmSA2NYxuaI6dum+OJI6d64JrxtPAaGhrHFhjsJvq1GYgON13qZKreVJ8/1edMFgQPdT3q+/idBoOh6vUf5gHkUJ9xsHab7Hfq3yZrp0N9zmTfeaj+09DQ0ND48NAceejr0RypH7g0NDQOAwcLPNMNSB/1cw71uqn+Pp1gfLj4KNczndd+1HbWD1caGhoaRw6aI2f+eqbz2qOBI3VlQQ0NDQ0NDQ0NDQ0NjSOEj3yGy2AwDAHYPzOXo6GhoaHxCUdLpVKp/bgv4miB5kgNDQ2N4wYH5ceP/MCloaGhoaGhoaGhoaGhMTm0pVBDQ0NDQ0NDQ0NDQ+MIQT9waWhoaGhoaGhoaGhoHCHoBy4NDQ0NDQ0NDQ0NDY0jBP3ApaGhoaGhoaGhoaGhcYSgH7g0NDQ0NDQ0NDQ0NDSOEPQDl4aGhoaGhoaGhoaGxhHCR37gMhgMK2fiQj4uGAyGFQaDIfxxX8eHgcFgWHkUX/tnDAZD4OO+jg8Lg8Gw6ihu+88e5W1/+tHa9sDR3/4a08PRzJGaHz8+HM0ceTTzI3D0x+ijmSOP9rafCroOl4aGhoaGhoaGhoaGxhGCthRqaGhoaGhoaGhoaGgcIegHLg0NDQ0NDQ0NDQ0NjSME/cCloaGhoaGhoaGhoaFxhKAfuDQ0NDQ0NDQ0NDQ0NI4Q9AOXhoaGhoaGhoaGhobGEcL/AXSwPC8Odq4zAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + }, + { + "output_type": "display_data", + "data": { + "image/png": "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UVW1LMt3LsvygbW9sPlPta3MVW3vCJF+qbbbEX8bLujeYvcgb036TGm3TfBjquqFY4zfuSzLT/FlH7tm31NVzx54A+EV9BlV9Sdru61jRp9SWzB88nV5bWpqamp6wtC/rKrbVfUhK21+trYXSqK32x27DjnO/XRV/QRw8WnLsjxlWZbnVp1vn//yqvrqqvozY4x3XBlrPumW/kFt5fv03bHnADe/pq4nO8f87tpuT/wzK83+Tm1fcvVHrstrU9O9Ul9wNT0R6Sm1vVB6zRjjLWt7UVJVVWOMXzPG+ODdBc3t2m6bONudfmVV/Xptv9u95OIrqupLdnvRa4zx68YYz360jO32t/+t2n8OjPSy2oLZN4wx3nmMsRljPGOM8cljjOeG8X6sqv5+Vf2FlTm/r6p+pKr+6KPlu6mpqanp8UnLsvxybTHlr48xtMvjaIzxnDHGF+6afW1VfeoY45lj+xMjn15Vf/eaU7yyqt4B33+wql63e2HUk8YYB2OMdxljvPvu/CfX9sLqRbV9wdNXj4u3A/pY16HPr6o/OcZ4Gz9xTdmdPqWqPnE22e5O32dU1SfdJZ9NTY+a+oKr6YlIX1pVT6rtHarvr+02QNGmql5S28req2v7bNfH7M7906r6D1X182OMX9od+6TaVsO+f7c98Xtqvq/9bvh77hjjt/uJ3XaGD6jtnbfvru1LL36wqt6qqn5gMt5n1267xQp9am1f7tHU1NTUdJ/RsixfXFts+9TaPqf001X156rqH+2afG5tX7b076vqh6vqh+r6r0D/21X1W/Gc9GlVfVBtX8DxE7XF2r9VVW8xxvjdOz4+atfuC2p78fXSNNY1Zfvh2j4a8Jcm56+S3dv/i9ri6hp9bV3/eeqmpnumsSz9m29NTU1NTU1NTU1NTU2PBfUdrqampqampqampqampseI+oKrqampqampqampqanpMaK+4GpqampqampqampqanqMqC+4mpqampqampqampqaHiO67g/JTWmMsYwxalmWGmOcH+fLONJx9QnjXeo/mffKdmmOGS+J5+u8UITjXYefNM9V/dJc0neSbzb+jFe3X2p7XXKdXtX20cwz63cvfN8r/WrO/aaU81eDZn7J81f5Muk68T2LQz8+8zsf8zr5w9un+a86dzdtruLBx0rj7dr/0rIsz6yma1FjZGPkbMzrtG2MfHzP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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + }, + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + }, + { + "output_type": "display_data", + "data": { + "image/png": "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1FNPxfz582EYBtasWYPf/e53yGazOP/883HBBRdIqtdYLIbVq1dj6dKlkpXqrbfewquvvgqTyYQ5c+bgsssukwxgB1uXbMvIyAjeeecdvPDCCyiVSpgxYwauu+46OJ1OfPrpp3j33XeRz+cxc+ZMnH766fLs0e+VSCSwevVqCQ4uFos49dRTcdppp+HDDz/E//3f/0kb582bJ6R9oBuDnp4erFy5EnPmzMF5552HWCyG5557Dhs3bkQgEMBFF12EefPmCTFpG/SHgeZIzZEaxw80R2qOPBKM6YGLWW0mT56MH/7wh0gmk2hvb0ckEpETJD+Xy+XwxhtvwGw244477kB9fT2effZZ7NixQ9KPrly5Elu2bIHH48GSJUuwePFibNu2DS+88AI6Ojpwyimn4KqrrsJbb72FzZs349Zbb4XdbseDDz6Ik046CTfccAOee+45vPfeeyiVSpg/fz6uuOIK9PX14ZFHHkEkEsHg4CDq6+tRX1+P119/HR0dHbDb7fj5z3+O22+/HWeeeaZkJgGqg/fFF1/gP//zP/H555+jsbERl112GebMmYONGzfi2WefRTwex7x58zBz5ky89tpr6OjoAAAEg0H09PTg/PPPx/e//33JBKSh8U1FpVJNVfz444/jiy++QEtLC/71X/8VHo8HixYtwj333AObzYZp06bhb//2bzE0NIRbb731K1f15XIZX375JR599FF0d3ejqakJv/71r+FyuTBx4kT09PQgm82iq6sLr732Gm655RYhE5VIyuUy9u7diwcffBCbN29GfX09XnrpJdx6660wm814+OGH0djYiMHBQfz0pz9FW1sbJk+ejHfeeQevvfYaXn75ZZx33nkIh8PYuHEjVqxYgWg0inA4jF/84hfw+/24+OKLARyaTAzDwObNm/HDH/4QTU1NmDBhAn7605/C6/Xi9NNPx9///d+jo6MDHo8Hq1evxl133YXzzjsPQ0ND2L59O0KhEJqbm2G1WtHR0YEVK1Zg1qxZQnQ2mw1OpxMrVqxALpeDy+XCG2+8gfvuuw9nnHEGkskkdu7ciWg0isbGRsnmVi6X8cQTT+Af/uEf8KMf/QiLFi3CSy+9hLvvvhuXX345Nm7ciA8//BCPPvooTj31VL2h/QNCc6TmSI3jC5ojD9wvmiNrMaYHLvVq1ev1YvLkydi8eTPi8fhX/Fuz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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "lMombPr0GF9a", + "colab_type": "text" + }, + "source": [ + "The images used in this demo are from the [Snapshot Serengeti dataset](http://lila.science/datasets/snapshot-serengeti), and released under the [Community Data License Agreement (permissive variant)](https://cdla.io/permissive-1-0/)." + ] + } + ] +} \ No newline at end of file diff --git a/workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tf2_colab.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tf2_colab.ipynb new file mode 100644 index 0000000..a779528 --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tf2_colab.ipynb @@ -0,0 +1,685 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rOvvWAVTkMR7" + }, + "source": [ + "# Eager Few Shot Object Detection Colab\n", + "\n", + "Welcome to the Eager Few Shot Object Detection Colab --- in this colab we demonstrate fine tuning of a (TF2 friendly) RetinaNet architecture on very few examples of a novel class after initializing from a pre-trained COCO checkpoint.\n", + "Training runs in eager mode.\n", + "\n", + "Estimated time to run through this colab (with GPU): \u003c 5 minutes." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vPs64QA1Zdov" + }, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "LBZ9VWZZFUCT" + }, + "outputs": [], + "source": [ + "!pip install -U --pre tensorflow==\"2.2.0\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oi28cqGGFWnY" + }, + "outputs": [], + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "# Clone the tensorflow models repository if it doesn't already exist\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "NwdsBdGhFanc" + }, + "outputs": [], + "source": [ + "# Install the Object Detection API\n", + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=.\n", + "cp object_detection/packages/tf2/setup.py .\n", + "python -m pip install ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "uZcqD4NLdnf4" + }, + "outputs": [], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import os\n", + "import random\n", + "import io\n", + "import imageio\n", + "import glob\n", + "import scipy.misc\n", + "import numpy as np\n", + "from six import BytesIO\n", + "from PIL import Image, ImageDraw, ImageFont\n", + "from IPython.display import display, Javascript\n", + "from IPython.display import Image as IPyImage\n", + "\n", + "import tensorflow as tf\n", + "\n", + "from object_detection.utils import label_map_util\n", + "from object_detection.utils import config_util\n", + "from object_detection.utils import visualization_utils as viz_utils\n", + "from object_detection.utils import colab_utils\n", + "from object_detection.builders import model_builder\n", + "\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "IogyryF2lFBL" + }, + "source": [ + "# Utilities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-y9R0Xllefec" + }, + "outputs": [], + "source": [ + "def load_image_into_numpy_array(path):\n", + " \"\"\"Load an image from file into a numpy array.\n", + "\n", + " Puts image into numpy array to feed into tensorflow graph.\n", + " Note that by convention we put it into a numpy array with shape\n", + " (height, width, channels), where channels=3 for RGB.\n", + "\n", + " Args:\n", + " path: a file path.\n", + "\n", + " Returns:\n", + " uint8 numpy array with shape (img_height, img_width, 3)\n", + " \"\"\"\n", + " img_data = tf.io.gfile.GFile(path, 'rb').read()\n", + " image = Image.open(BytesIO(img_data))\n", + " (im_width, im_height) = image.size\n", + " return np.array(image.getdata()).reshape(\n", + " (im_height, im_width, 3)).astype(np.uint8)\n", + "\n", + "def plot_detections(image_np,\n", + " boxes,\n", + " classes,\n", + " scores,\n", + " category_index,\n", + " figsize=(12, 16),\n", + " image_name=None):\n", + " \"\"\"Wrapper function to visualize detections.\n", + "\n", + " Args:\n", + " image_np: uint8 numpy array with shape (img_height, img_width, 3)\n", + " boxes: a numpy array of shape [N, 4]\n", + " classes: a numpy array of shape [N]. Note that class indices are 1-based,\n", + " and match the keys in the label map.\n", + " scores: a numpy array of shape [N] or None. If scores=None, then\n", + " this function assumes that the boxes to be plotted are groundtruth\n", + " boxes and plot all boxes as black with no classes or scores.\n", + " category_index: a dict containing category dictionaries (each holding\n", + " category index `id` and category name `name`) keyed by category indices.\n", + " figsize: size for the figure.\n", + " image_name: a name for the image file.\n", + " \"\"\"\n", + " image_np_with_annotations = image_np.copy()\n", + " viz_utils.visualize_boxes_and_labels_on_image_array(\n", + " image_np_with_annotations,\n", + " boxes,\n", + " classes,\n", + " scores,\n", + " category_index,\n", + " use_normalized_coordinates=True,\n", + " min_score_thresh=0.8)\n", + " if image_name:\n", + " plt.imsave(image_name, image_np_with_annotations)\n", + " else:\n", + " plt.imshow(image_np_with_annotations)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "sSaXL28TZfk1" + }, + "source": [ + "# Rubber Ducky data\n", + "\n", + "We will start with some toy (literally) data consisting of 5 images of a rubber\n", + "ducky. Note that the [coco](https://cocodataset.org/#explore) dataset contains a number of animals, but notably, it does *not* contain rubber duckies (or even ducks for that matter), so this is a novel class." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "SQy3ND7EpFQM" + }, + "outputs": [], + "source": [ + "# Load images and visualize\n", + "train_image_dir = 'models/research/object_detection/test_images/ducky/train/'\n", + "train_images_np = []\n", + "for i in range(1, 6):\n", + " image_path = os.path.join(train_image_dir, 'robertducky' + str(i) + '.jpg')\n", + " train_images_np.append(load_image_into_numpy_array(image_path))\n", + "\n", + "plt.rcParams['axes.grid'] = False\n", + "plt.rcParams['xtick.labelsize'] = False\n", + "plt.rcParams['ytick.labelsize'] = False\n", + "plt.rcParams['xtick.top'] = False\n", + "plt.rcParams['xtick.bottom'] = False\n", + "plt.rcParams['ytick.left'] = False\n", + "plt.rcParams['ytick.right'] = False\n", + "plt.rcParams['figure.figsize'] = [14, 7]\n", + "\n", + "for idx, train_image_np in enumerate(train_images_np):\n", + " plt.subplot(2, 3, idx+1)\n", + " plt.imshow(train_image_np)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cbKXmQoxcUgE" + }, + "source": [ + "# Annotate images with bounding boxes\n", + "\n", + "In this cell you will annotate the rubber duckies --- draw a box around the rubber ducky in each image; click `next image` to go to the next image and `submit` when there are no more images.\n", + "\n", + "If you'd like to skip the manual annotation step, we totally understand. In this case, simply skip this cell and run the next cell instead, where we've prepopulated the groundtruth with pre-annotated bounding boxes.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-nEDRoUEcUgL" + }, + "outputs": [], + "source": [ + "gt_boxes = []\n", + "colab_utils.annotate(train_images_np, box_storage_pointer=gt_boxes)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "wTP9AFqecUgS" + }, + "source": [ + "# In case you didn't want to label...\n", + "\n", + "Run this cell only if you didn't annotate anything above and\n", + "would prefer to just use our preannotated boxes. Don't forget\n", + "to uncomment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "wIAT6ZUmdHOC" + }, + "outputs": [], + "source": [ + "# gt_boxes = [\n", + "# np.array([[0.436, 0.591, 0.629, 0.712]], dtype=np.float32),\n", + "# np.array([[0.539, 0.583, 0.73, 0.71]], dtype=np.float32),\n", + "# np.array([[0.464, 0.414, 0.626, 0.548]], dtype=np.float32),\n", + "# np.array([[0.313, 0.308, 0.648, 0.526]], dtype=np.float32),\n", + "# np.array([[0.256, 0.444, 0.484, 0.629]], dtype=np.float32)\n", + "# ]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Dqb_yjAo3cO_" + }, + "source": [ + "# Prepare data for training\n", + "\n", + "Below we add the class annotations (for simplicity, we assume a single class in this colab; though it should be straightforward to extend this to handle multiple classes). We also convert everything to the format that the training\n", + "loop below expects (e.g., everything converted to tensors, classes converted to one-hot representations, etc.)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "HWBqFVMcweF-" + }, + "outputs": [], + "source": [ + "\n", + "# By convention, our non-background classes start counting at 1. Given\n", + "# that we will be predicting just one class, we will therefore assign it a\n", + "# `class id` of 1.\n", + "duck_class_id = 1\n", + "num_classes = 1\n", + "\n", + "category_index = {duck_class_id: {'id': duck_class_id, 'name': 'rubber_ducky'}}\n", + "\n", + "# Convert class labels to one-hot; convert everything to tensors.\n", + "# The `label_id_offset` here shifts all classes by a certain number of indices;\n", + "# we do this here so that the model receives one-hot labels where non-background\n", + "# classes start counting at the zeroth index. This is ordinarily just handled\n", + "# automatically in our training binaries, but we need to reproduce it here.\n", + "label_id_offset = 1\n", + "train_image_tensors = []\n", + "gt_classes_one_hot_tensors = []\n", + "gt_box_tensors = []\n", + "for (train_image_np, gt_box_np) in zip(\n", + " train_images_np, gt_boxes):\n", + " train_image_tensors.append(tf.expand_dims(tf.convert_to_tensor(\n", + " train_image_np, dtype=tf.float32), axis=0))\n", + " gt_box_tensors.append(tf.convert_to_tensor(gt_box_np, dtype=tf.float32))\n", + " zero_indexed_groundtruth_classes = tf.convert_to_tensor(\n", + " np.ones(shape=[gt_box_np.shape[0]], dtype=np.int32) - label_id_offset)\n", + " gt_classes_one_hot_tensors.append(tf.one_hot(\n", + " zero_indexed_groundtruth_classes, num_classes))\n", + "print('Done prepping data.')\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "b3_Z3mJWN9KJ" + }, + "source": [ + "# Let's just visualize the rubber duckies as a sanity check\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "YBD6l-E4N71y" + }, + "outputs": [], + "source": [ + "dummy_scores = np.array([1.0], dtype=np.float32) # give boxes a score of 100%\n", + "\n", + "plt.figure(figsize=(30, 15))\n", + "for idx in range(5):\n", + " plt.subplot(2, 3, idx+1)\n", + " plot_detections(\n", + " train_images_np[idx],\n", + " gt_boxes[idx],\n", + " np.ones(shape=[gt_boxes[idx].shape[0]], dtype=np.int32),\n", + " dummy_scores, category_index)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "ghDAsqfoZvPh" + }, + "source": [ + "# Create model and restore weights for all but last layer\n", + "\n", + "In this cell we build a single stage detection architecture (RetinaNet) and restore all but the classification layer at the top (which will be automatically randomly initialized).\n", + "\n", + "For simplicity, we have hardcoded a number of things in this colab for the specific RetinaNet architecture at hand (including assuming that the image size will always be 640x640), however it is not difficult to generalize to other model configurations." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "9J16r3NChD-7" + }, + "outputs": [], + "source": [ + "# Download the checkpoint and put it into models/research/object_detection/test_data/\n", + "\n", + "!wget http://download.tensorflow.org/models/object_detection/tf2/20200711/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.tar.gz\n", + "!tar -xf ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.tar.gz\n", + "!mv ssd_resnet50_v1_fpn_640x640_coco17_tpu-8/checkpoint models/research/object_detection/test_data/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "RyT4BUbaMeG-" + }, + "outputs": [], + "source": [ + "tf.keras.backend.clear_session()\n", + "\n", + "print('Building model and restoring weights for fine-tuning...', flush=True)\n", + "num_classes = 1\n", + "pipeline_config = 'models/research/object_detection/configs/tf2/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.config'\n", + "checkpoint_path = 'models/research/object_detection/test_data/checkpoint/ckpt-0'\n", + "\n", + "# Load pipeline config and build a detection model.\n", + "#\n", + "# Since we are working off of a COCO architecture which predicts 90\n", + "# class slots by default, we override the `num_classes` field here to be just\n", + "# one (for our new rubber ducky class).\n", + "configs = config_util.get_configs_from_pipeline_file(pipeline_config)\n", + "model_config = configs['model']\n", + "model_config.ssd.num_classes = num_classes\n", + "model_config.ssd.freeze_batchnorm = True\n", + "detection_model = model_builder.build(\n", + " model_config=model_config, is_training=True)\n", + "\n", + "# Set up object-based checkpoint restore --- RetinaNet has two prediction\n", + "# `heads` --- one for classification, the other for box regression. We will\n", + "# restore the box regression head but initialize the classification head\n", + "# from scratch (we show the omission below by commenting out the line that\n", + "# we would add if we wanted to restore both heads)\n", + "fake_box_predictor = tf.compat.v2.train.Checkpoint(\n", + " _base_tower_layers_for_heads=detection_model._box_predictor._base_tower_layers_for_heads,\n", + " # _prediction_heads=detection_model._box_predictor._prediction_heads,\n", + " # (i.e., the classification head that we *will not* restore)\n", + " _box_prediction_head=detection_model._box_predictor._box_prediction_head,\n", + " )\n", + "fake_model = tf.compat.v2.train.Checkpoint(\n", + " _feature_extractor=detection_model._feature_extractor,\n", + " _box_predictor=fake_box_predictor)\n", + "ckpt = tf.compat.v2.train.Checkpoint(model=fake_model)\n", + "ckpt.restore(checkpoint_path).expect_partial()\n", + "\n", + "# Run model through a dummy image so that variables are created\n", + "image, shapes = detection_model.preprocess(tf.zeros([1, 640, 640, 3]))\n", + "prediction_dict = detection_model.predict(image, shapes)\n", + "_ = detection_model.postprocess(prediction_dict, shapes)\n", + "print('Weights restored!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "pCkWmdoZZ0zJ" + }, + "source": [ + "# Eager mode custom training loop\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "nyHoF4mUrv5-" + }, + "outputs": [], + "source": [ + "tf.keras.backend.set_learning_phase(True)\n", + "\n", + "# These parameters can be tuned; since our training set has 5 images\n", + "# it doesn't make sense to have a much larger batch size, though we could\n", + "# fit more examples in memory if we wanted to.\n", + "batch_size = 4\n", + "learning_rate = 0.01\n", + "num_batches = 100\n", + "\n", + "# Select variables in top layers to fine-tune.\n", + "trainable_variables = detection_model.trainable_variables\n", + "to_fine_tune = []\n", + "prefixes_to_train = [\n", + " 'WeightSharedConvolutionalBoxPredictor/WeightSharedConvolutionalBoxHead',\n", + " 'WeightSharedConvolutionalBoxPredictor/WeightSharedConvolutionalClassHead']\n", + "for var in trainable_variables:\n", + " if any([var.name.startswith(prefix) for prefix in prefixes_to_train]):\n", + " to_fine_tune.append(var)\n", + "\n", + "# Set up forward + backward pass for a single train step.\n", + "def get_model_train_step_function(model, optimizer, vars_to_fine_tune):\n", + " \"\"\"Get a tf.function for training step.\"\"\"\n", + "\n", + " # Use tf.function for a bit of speed.\n", + " # Comment out the tf.function decorator if you want the inside of the\n", + " # function to run eagerly.\n", + " @tf.function\n", + " def train_step_fn(image_tensors,\n", + " groundtruth_boxes_list,\n", + " groundtruth_classes_list):\n", + " \"\"\"A single training iteration.\n", + "\n", + " Args:\n", + " image_tensors: A list of [1, height, width, 3] Tensor of type tf.float32.\n", + " Note that the height and width can vary across images, as they are\n", + " reshaped within this function to be 640x640.\n", + " groundtruth_boxes_list: A list of Tensors of shape [N_i, 4] with type\n", + " tf.float32 representing groundtruth boxes for each image in the batch.\n", + " groundtruth_classes_list: A list of Tensors of shape [N_i, num_classes]\n", + " with type tf.float32 representing groundtruth boxes for each image in\n", + " the batch.\n", + "\n", + " Returns:\n", + " A scalar tensor representing the total loss for the input batch.\n", + " \"\"\"\n", + " shapes = tf.constant(batch_size * [[640, 640, 3]], dtype=tf.int32)\n", + " model.provide_groundtruth(\n", + " groundtruth_boxes_list=groundtruth_boxes_list,\n", + " groundtruth_classes_list=groundtruth_classes_list)\n", + " with tf.GradientTape() as tape:\n", + " preprocessed_images = tf.concat(\n", + " [detection_model.preprocess(image_tensor)[0]\n", + " for image_tensor in image_tensors], axis=0)\n", + " prediction_dict = model.predict(preprocessed_images, shapes)\n", + " losses_dict = model.loss(prediction_dict, shapes)\n", + " total_loss = losses_dict['Loss/localization_loss'] + losses_dict['Loss/classification_loss']\n", + " gradients = tape.gradient(total_loss, vars_to_fine_tune)\n", + " optimizer.apply_gradients(zip(gradients, vars_to_fine_tune))\n", + " return total_loss\n", + "\n", + " return train_step_fn\n", + "\n", + "optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=0.9)\n", + "train_step_fn = get_model_train_step_function(\n", + " detection_model, optimizer, to_fine_tune)\n", + "\n", + "print('Start fine-tuning!', flush=True)\n", + "for idx in range(num_batches):\n", + " # Grab keys for a random subset of examples\n", + " all_keys = list(range(len(train_images_np)))\n", + " random.shuffle(all_keys)\n", + " example_keys = all_keys[:batch_size]\n", + "\n", + " # Note that we do not do data augmentation in this demo. If you want a\n", + " # a fun exercise, we recommend experimenting with random horizontal flipping\n", + " # and random cropping :)\n", + " gt_boxes_list = [gt_box_tensors[key] for key in example_keys]\n", + " gt_classes_list = [gt_classes_one_hot_tensors[key] for key in example_keys]\n", + " image_tensors = [train_image_tensors[key] for key in example_keys]\n", + "\n", + " # Training step (forward pass + backwards pass)\n", + " total_loss = train_step_fn(image_tensors, gt_boxes_list, gt_classes_list)\n", + "\n", + " if idx % 10 == 0:\n", + " print('batch ' + str(idx) + ' of ' + str(num_batches)\n", + " + ', loss=' + str(total_loss.numpy()), flush=True)\n", + "\n", + "print('Done fine-tuning!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "WHlXL1x_Z3tc" + }, + "source": [ + "# Load test images and run inference with new model!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "WcE6OwrHQJya" + }, + "outputs": [], + "source": [ + "test_image_dir = 'models/research/object_detection/test_images/ducky/test/'\n", + "test_images_np = []\n", + "for i in range(1, 50):\n", + " image_path = os.path.join(test_image_dir, 'out' + str(i) + '.jpg')\n", + " test_images_np.append(np.expand_dims(\n", + " load_image_into_numpy_array(image_path), axis=0))\n", + "\n", + "# Again, uncomment this decorator if you want to run inference eagerly\n", + "@tf.function\n", + "def detect(input_tensor):\n", + " \"\"\"Run detection on an input image.\n", + "\n", + " Args:\n", + " input_tensor: A [1, height, width, 3] Tensor of type tf.float32.\n", + " Note that height and width can be anything since the image will be\n", + " immediately resized according to the needs of the model within this\n", + " function.\n", + "\n", + " Returns:\n", + " A dict containing 3 Tensors (`detection_boxes`, `detection_classes`,\n", + " and `detection_scores`).\n", + " \"\"\"\n", + " preprocessed_image, shapes = detection_model.preprocess(input_tensor)\n", + " prediction_dict = detection_model.predict(preprocessed_image, shapes)\n", + " return detection_model.postprocess(prediction_dict, shapes)\n", + "\n", + "# Note that the first frame will trigger tracing of the tf.function, which will\n", + "# take some time, after which inference should be fast.\n", + "\n", + "label_id_offset = 1\n", + "for i in range(len(test_images_np)):\n", + " input_tensor = tf.convert_to_tensor(test_images_np[i], dtype=tf.float32)\n", + " detections = detect(input_tensor)\n", + "\n", + " plot_detections(\n", + " test_images_np[i][0],\n", + " detections['detection_boxes'][0].numpy(),\n", + " detections['detection_classes'][0].numpy().astype(np.uint32)\n", + " + label_id_offset,\n", + " detections['detection_scores'][0].numpy(),\n", + " category_index, figsize=(15, 20), image_name=\"gif_frame_\" + ('%02d' % i) + \".jpg\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "RW1FrT2iNnpy" + }, + "outputs": [], + "source": [ + "imageio.plugins.freeimage.download()\n", + "\n", + "anim_file = 'duckies_test.gif'\n", + "\n", + "filenames = glob.glob('gif_frame_*.jpg')\n", + "filenames = sorted(filenames)\n", + "last = -1\n", + "images = []\n", + "for filename in filenames:\n", + " image = imageio.imread(filename)\n", + " images.append(image)\n", + "\n", + "imageio.mimsave(anim_file, images, 'GIF-FI', fps=5)\n", + "\n", + "display(IPyImage(open(anim_file, 'rb').read()))" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "interactive_eager_few_shot_od_training_colab.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tflite.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tflite.ipynb new file mode 100644 index 0000000..b47d4bd --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/eager_few_shot_od_training_tflite.ipynb @@ -0,0 +1,730 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "rOvvWAVTkMR7" + }, + "source": [ + "# Introduction\n", + "\n", + "Welcome to the **Few Shot Object Detection for TensorFlow Lite** Colab. Here, we demonstrate fine tuning of a SSD architecture (pre-trained on COCO) on very few examples of a *novel* class. We will then generate a (downloadable) TensorFlow Lite model for on-device inference.\n", + "\n", + "**NOTE:** This Colab is meant for the few-shot detection use-case. To train a model on a large dataset, please follow the [TF2 training](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_training_and_evaluation.md#training) documentation and then [convert](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/running_on_mobile_tf2.md) the model to TensorFlow Lite." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "3U2sv0upw04O" + }, + "source": [ + "# Set Up" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vPs64QA1Zdov" + }, + "source": [ + "## Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "H0rKBV4uZacD" + }, + "outputs": [], + "source": [ + "# Support for TF2 models was added after TF 2.3.\n", + "!pip install tf-nightly" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "oi28cqGGFWnY" + }, + "outputs": [], + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "# Clone the tensorflow models repository if it doesn't already exist\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "NwdsBdGhFanc" + }, + "outputs": [], + "source": [ + "# Install the Object Detection API\n", + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=.\n", + "cp object_detection/packages/tf2/setup.py .\n", + "python -m pip install ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uZcqD4NLdnf4" + }, + "outputs": [], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import os\n", + "import random\n", + "import io\n", + "import imageio\n", + "import glob\n", + "import scipy.misc\n", + "import numpy as np\n", + "from six import BytesIO\n", + "from PIL import Image, ImageDraw, ImageFont\n", + "from IPython.display import display, Javascript\n", + "from IPython.display import Image as IPyImage\n", + "\n", + "import tensorflow as tf\n", + "\n", + "from object_detection.utils import label_map_util\n", + "from object_detection.utils import config_util\n", + "from object_detection.utils import visualization_utils as viz_utils\n", + "from object_detection.utils import colab_utils\n", + "from object_detection.utils import config_util\n", + "from object_detection.builders import model_builder\n", + "\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IogyryF2lFBL" + }, + "source": [ + "##Utilities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-y9R0Xllefec" + }, + "outputs": [], + "source": [ + "def load_image_into_numpy_array(path):\n", + " \"\"\"Load an image from file into a numpy array.\n", + "\n", + " Puts image into numpy array to feed into tensorflow graph.\n", + " Note that by convention we put it into a numpy array with shape\n", + " (height, width, channels), where channels=3 for RGB.\n", + "\n", + " Args:\n", + " path: a file path.\n", + "\n", + " Returns:\n", + " uint8 numpy array with shape (img_height, img_width, 3)\n", + " \"\"\"\n", + " img_data = tf.io.gfile.GFile(path, 'rb').read()\n", + " image = Image.open(BytesIO(img_data))\n", + " (im_width, im_height) = image.size\n", + " return np.array(image.getdata()).reshape(\n", + " (im_height, im_width, 3)).astype(np.uint8)\n", + "\n", + "def plot_detections(image_np,\n", + " boxes,\n", + " classes,\n", + " scores,\n", + " category_index,\n", + " figsize=(12, 16),\n", + " image_name=None):\n", + " \"\"\"Wrapper function to visualize detections.\n", + "\n", + " Args:\n", + " image_np: uint8 numpy array with shape (img_height, img_width, 3)\n", + " boxes: a numpy array of shape [N, 4]\n", + " classes: a numpy array of shape [N]. Note that class indices are 1-based,\n", + " and match the keys in the label map.\n", + " scores: a numpy array of shape [N] or None. If scores=None, then\n", + " this function assumes that the boxes to be plotted are groundtruth\n", + " boxes and plot all boxes as black with no classes or scores.\n", + " category_index: a dict containing category dictionaries (each holding\n", + " category index `id` and category name `name`) keyed by category indices.\n", + " figsize: size for the figure.\n", + " image_name: a name for the image file.\n", + " \"\"\"\n", + " image_np_with_annotations = image_np.copy()\n", + " viz_utils.visualize_boxes_and_labels_on_image_array(\n", + " image_np_with_annotations,\n", + " boxes,\n", + " classes,\n", + " scores,\n", + " category_index,\n", + " use_normalized_coordinates=True,\n", + " min_score_thresh=0.8)\n", + " if image_name:\n", + " plt.imsave(image_name, image_np_with_annotations)\n", + " else:\n", + " plt.imshow(image_np_with_annotations)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sSaXL28TZfk1" + }, + "source": [ + "## Rubber Ducky data\n", + "\n", + "We will start with some toy data consisting of 5 images of a rubber\n", + "ducky. Note that the [COCO](https://cocodataset.org/#explore) dataset contains a number of animals, but notably, it does *not* contain rubber duckies (or even ducks for that matter), so this is a novel class." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "SQy3ND7EpFQM" + }, + "outputs": [], + "source": [ + "# Load images and visualize\n", + "train_image_dir = 'models/research/object_detection/test_images/ducky/train/'\n", + "train_images_np = []\n", + "for i in range(1, 6):\n", + " image_path = os.path.join(train_image_dir, 'robertducky' + str(i) + '.jpg')\n", + " train_images_np.append(load_image_into_numpy_array(image_path))\n", + "\n", + "plt.rcParams['axes.grid'] = False\n", + "plt.rcParams['xtick.labelsize'] = False\n", + "plt.rcParams['ytick.labelsize'] = False\n", + "plt.rcParams['xtick.top'] = False\n", + "plt.rcParams['xtick.bottom'] = False\n", + "plt.rcParams['ytick.left'] = False\n", + "plt.rcParams['ytick.right'] = False\n", + "plt.rcParams['figure.figsize'] = [14, 7]\n", + "\n", + "for idx, train_image_np in enumerate(train_images_np):\n", + " plt.subplot(2, 3, idx+1)\n", + " plt.imshow(train_image_np)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LbOe9Ym7xMGV" + }, + "source": [ + "# Transfer Learning\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Dqb_yjAo3cO_" + }, + "source": [ + "## Data Preparation\n", + "\n", + "First, we populate the groundtruth with pre-annotated bounding boxes.\n", + "\n", + "We then add the class annotations (for simplicity, we assume a single 'Duck' class in this colab; though it should be straightforward to extend this to handle multiple classes). We also convert everything to the format that the training\n", + "loop below expects (e.g., everything converted to tensors, classes converted to one-hot representations, etc.)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "wIAT6ZUmdHOC" + }, + "outputs": [], + "source": [ + "gt_boxes = [\n", + " np.array([[0.436, 0.591, 0.629, 0.712]], dtype=np.float32),\n", + " np.array([[0.539, 0.583, 0.73, 0.71]], dtype=np.float32),\n", + " np.array([[0.464, 0.414, 0.626, 0.548]], dtype=np.float32),\n", + " np.array([[0.313, 0.308, 0.648, 0.526]], dtype=np.float32),\n", + " np.array([[0.256, 0.444, 0.484, 0.629]], dtype=np.float32)\n", + "]\n", + "\n", + "# By convention, our non-background classes start counting at 1. Given\n", + "# that we will be predicting just one class, we will therefore assign it a\n", + "# `class id` of 1.\n", + "duck_class_id = 1\n", + "num_classes = 1\n", + "\n", + "category_index = {duck_class_id: {'id': duck_class_id, 'name': 'rubber_ducky'}}\n", + "\n", + "# Convert class labels to one-hot; convert everything to tensors.\n", + "# The `label_id_offset` here shifts all classes by a certain number of indices;\n", + "# we do this here so that the model receives one-hot labels where non-background\n", + "# classes start counting at the zeroth index. This is ordinarily just handled\n", + "# automatically in our training binaries, but we need to reproduce it here.\n", + "label_id_offset = 1\n", + "train_image_tensors = []\n", + "gt_classes_one_hot_tensors = []\n", + "gt_box_tensors = []\n", + "for (train_image_np, gt_box_np) in zip(\n", + " train_images_np, gt_boxes):\n", + " train_image_tensors.append(tf.expand_dims(tf.convert_to_tensor(\n", + " train_image_np, dtype=tf.float32), axis=0))\n", + " gt_box_tensors.append(tf.convert_to_tensor(gt_box_np, dtype=tf.float32))\n", + " zero_indexed_groundtruth_classes = tf.convert_to_tensor(\n", + " np.ones(shape=[gt_box_np.shape[0]], dtype=np.int32) - label_id_offset)\n", + " gt_classes_one_hot_tensors.append(tf.one_hot(\n", + " zero_indexed_groundtruth_classes, num_classes))\n", + "print('Done prepping data.')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "b3_Z3mJWN9KJ" + }, + "source": [ + "Let's just visualize the rubber duckies as a sanity check\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "YBD6l-E4N71y" + }, + "outputs": [], + "source": [ + "dummy_scores = np.array([1.0], dtype=np.float32) # give boxes a score of 100%\n", + "\n", + "plt.figure(figsize=(30, 15))\n", + "for idx in range(5):\n", + " plt.subplot(2, 3, idx+1)\n", + " plot_detections(\n", + " train_images_np[idx],\n", + " gt_boxes[idx],\n", + " np.ones(shape=[gt_boxes[idx].shape[0]], dtype=np.int32),\n", + " dummy_scores, category_index)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ghDAsqfoZvPh" + }, + "source": [ + "## Load mobile-friendly model\n", + "\n", + "In this cell we build a mobile-friendly single-stage detection architecture (SSD MobileNet V2 FPN-Lite) and restore all but the classification layer at the top (which will be randomly initialized).\n", + "\n", + "**NOTE**: TensorFlow Lite only supports SSD models for now.\n", + "\n", + "For simplicity, we have hardcoded a number of things in this colab for the specific SSD architecture at hand (including assuming that the image size will always be 320x320), however it is not difficult to generalize to other model configurations (`pipeline.config` in the zip downloaded from the [Model Zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.)).\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "9J16r3NChD-7" + }, + "outputs": [], + "source": [ + "# Download the checkpoint and put it into models/research/object_detection/test_data/\n", + "\n", + "!wget http://download.tensorflow.org/models/object_detection/tf2/20200711/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz\n", + "!tar -xf ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz\n", + "!if [ -d \"models/research/object_detection/test_data/checkpoint\" ]; then rm -Rf models/research/object_detection/test_data/checkpoint; fi\n", + "!mkdir models/research/object_detection/test_data/checkpoint\n", + "!mv ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8/checkpoint models/research/object_detection/test_data/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "RyT4BUbaMeG-" + }, + "outputs": [], + "source": [ + "tf.keras.backend.clear_session()\n", + "\n", + "print('Building model and restoring weights for fine-tuning...', flush=True)\n", + "num_classes = 1\n", + "pipeline_config = 'models/research/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config'\n", + "checkpoint_path = 'models/research/object_detection/test_data/checkpoint/ckpt-0'\n", + "\n", + "# This will be where we save checkpoint \u0026 config for TFLite conversion later.\n", + "output_directory = 'output/'\n", + "output_checkpoint_dir = os.path.join(output_directory, 'checkpoint')\n", + "\n", + "# Load pipeline config and build a detection model.\n", + "#\n", + "# Since we are working off of a COCO architecture which predicts 90\n", + "# class slots by default, we override the `num_classes` field here to be just\n", + "# one (for our new rubber ducky class).\n", + "configs = config_util.get_configs_from_pipeline_file(pipeline_config)\n", + "model_config = configs['model']\n", + "model_config.ssd.num_classes = num_classes\n", + "model_config.ssd.freeze_batchnorm = True\n", + "detection_model = model_builder.build(\n", + " model_config=model_config, is_training=True)\n", + "# Save new pipeline config\n", + "pipeline_proto = config_util.create_pipeline_proto_from_configs(configs)\n", + "config_util.save_pipeline_config(pipeline_proto, output_directory)\n", + "\n", + "# Set up object-based checkpoint restore --- SSD has two prediction\n", + "# `heads` --- one for classification, the other for box regression. We will\n", + "# restore the box regression head but initialize the classification head\n", + "# from scratch (we show the omission below by commenting out the line that\n", + "# we would add if we wanted to restore both heads)\n", + "fake_box_predictor = tf.compat.v2.train.Checkpoint(\n", + " _base_tower_layers_for_heads=detection_model._box_predictor._base_tower_layers_for_heads,\n", + " # _prediction_heads=detection_model._box_predictor._prediction_heads,\n", + " # (i.e., the classification head that we *will not* restore)\n", + " _box_prediction_head=detection_model._box_predictor._box_prediction_head,\n", + " )\n", + "fake_model = tf.compat.v2.train.Checkpoint(\n", + " _feature_extractor=detection_model._feature_extractor,\n", + " _box_predictor=fake_box_predictor)\n", + "ckpt = tf.compat.v2.train.Checkpoint(model=fake_model)\n", + "ckpt.restore(checkpoint_path).expect_partial()\n", + "\n", + "# To save checkpoint for TFLite conversion.\n", + "exported_ckpt = tf.compat.v2.train.Checkpoint(model=detection_model)\n", + "ckpt_manager = tf.train.CheckpointManager(\n", + " exported_ckpt, output_checkpoint_dir, max_to_keep=1)\n", + "\n", + "# Run model through a dummy image so that variables are created\n", + "image, shapes = detection_model.preprocess(tf.zeros([1, 320, 320, 3]))\n", + "prediction_dict = detection_model.predict(image, shapes)\n", + "_ = detection_model.postprocess(prediction_dict, shapes)\n", + "print('Weights restored!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pCkWmdoZZ0zJ" + }, + "source": [ + "## Eager training loop (Fine-tuning)\n", + "\n", + "Some of the parameters in this block have been set empirically: for example, `learning_rate`, `num_batches` \u0026 `momentum` for SGD. These are just a starting point, you will have to tune these for your data \u0026 model architecture to get the best results.\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "nyHoF4mUrv5-" + }, + "outputs": [], + "source": [ + "tf.keras.backend.set_learning_phase(True)\n", + "\n", + "# These parameters can be tuned; since our training set has 5 images\n", + "# it doesn't make sense to have a much larger batch size, though we could\n", + "# fit more examples in memory if we wanted to.\n", + "batch_size = 5\n", + "learning_rate = 0.15\n", + "num_batches = 1000\n", + "\n", + "# Select variables in top layers to fine-tune.\n", + "trainable_variables = detection_model.trainable_variables\n", + "to_fine_tune = []\n", + "prefixes_to_train = [\n", + " 'WeightSharedConvolutionalBoxPredictor/WeightSharedConvolutionalBoxHead',\n", + " 'WeightSharedConvolutionalBoxPredictor/WeightSharedConvolutionalClassHead']\n", + "for var in trainable_variables:\n", + " if any([var.name.startswith(prefix) for prefix in prefixes_to_train]):\n", + " to_fine_tune.append(var)\n", + "\n", + "# Set up forward + backward pass for a single train step.\n", + "def get_model_train_step_function(model, optimizer, vars_to_fine_tune):\n", + " \"\"\"Get a tf.function for training step.\"\"\"\n", + "\n", + " # Use tf.function for a bit of speed.\n", + " # Comment out the tf.function decorator if you want the inside of the\n", + " # function to run eagerly.\n", + " @tf.function\n", + " def train_step_fn(image_tensors,\n", + " groundtruth_boxes_list,\n", + " groundtruth_classes_list):\n", + " \"\"\"A single training iteration.\n", + "\n", + " Args:\n", + " image_tensors: A list of [1, height, width, 3] Tensor of type tf.float32.\n", + " Note that the height and width can vary across images, as they are\n", + " reshaped within this function to be 320x320.\n", + " groundtruth_boxes_list: A list of Tensors of shape [N_i, 4] with type\n", + " tf.float32 representing groundtruth boxes for each image in the batch.\n", + " groundtruth_classes_list: A list of Tensors of shape [N_i, num_classes]\n", + " with type tf.float32 representing groundtruth boxes for each image in\n", + " the batch.\n", + "\n", + " Returns:\n", + " A scalar tensor representing the total loss for the input batch.\n", + " \"\"\"\n", + " shapes = tf.constant(batch_size * [[320, 320, 3]], dtype=tf.int32)\n", + " model.provide_groundtruth(\n", + " groundtruth_boxes_list=groundtruth_boxes_list,\n", + " groundtruth_classes_list=groundtruth_classes_list)\n", + " with tf.GradientTape() as tape:\n", + " preprocessed_images = tf.concat(\n", + " [detection_model.preprocess(image_tensor)[0]\n", + " for image_tensor in image_tensors], axis=0)\n", + " prediction_dict = model.predict(preprocessed_images, shapes)\n", + " losses_dict = model.loss(prediction_dict, shapes)\n", + " total_loss = losses_dict['Loss/localization_loss'] + losses_dict['Loss/classification_loss']\n", + " gradients = tape.gradient(total_loss, vars_to_fine_tune)\n", + " optimizer.apply_gradients(zip(gradients, vars_to_fine_tune))\n", + " return total_loss\n", + "\n", + " return train_step_fn\n", + "\n", + "optimizer = tf.keras.optimizers.SGD(learning_rate=learning_rate, momentum=0.9)\n", + "train_step_fn = get_model_train_step_function(\n", + " detection_model, optimizer, to_fine_tune)\n", + "\n", + "print('Start fine-tuning!', flush=True)\n", + "for idx in range(num_batches):\n", + " # Grab keys for a random subset of examples\n", + " all_keys = list(range(len(train_images_np)))\n", + " random.shuffle(all_keys)\n", + " example_keys = all_keys[:batch_size]\n", + "\n", + " # Note that we do not do data augmentation in this demo. If you want a\n", + " # a fun exercise, we recommend experimenting with random horizontal flipping\n", + " # and random cropping :)\n", + " gt_boxes_list = [gt_box_tensors[key] for key in example_keys]\n", + " gt_classes_list = [gt_classes_one_hot_tensors[key] for key in example_keys]\n", + " image_tensors = [train_image_tensors[key] for key in example_keys]\n", + "\n", + " # Training step (forward pass + backwards pass)\n", + " total_loss = train_step_fn(image_tensors, gt_boxes_list, gt_classes_list)\n", + "\n", + " if idx % 100 == 0:\n", + " print('batch ' + str(idx) + ' of ' + str(num_batches)\n", + " + ', loss=' + str(total_loss.numpy()), flush=True)\n", + "\n", + "print('Done fine-tuning!')\n", + "\n", + "ckpt_manager.save()\n", + "print('Checkpoint saved!')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cYk1_9Fc2lZO" + }, + "source": [ + "# Export \u0026 run with TensorFlow Lite\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "y0nsDVEd9SuX" + }, + "source": [ + "## Model Conversion\n", + "\n", + "First, we invoke the `export_tflite_graph_tf2.py` script to generate a TFLite-friendly intermediate SavedModel. This will then be passed to the TensorFlow Lite Converter for generating the final model.\n", + "\n", + "To know more about this process, please look at [this documentation](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/running_on_mobile_tf2.md)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dyrqHSQQ7WKE" + }, + "outputs": [], + "source": [ + "%%bash\n", + "python models/research/object_detection/export_tflite_graph_tf2.py \\\n", + " --pipeline_config_path output/pipeline.config \\\n", + " --trained_checkpoint_dir output/checkpoint \\\n", + " --output_directory tflite" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "m5hjPyR78bgs" + }, + "outputs": [], + "source": [ + "!tflite_convert --saved_model_dir=tflite/saved_model --output_file=tflite/model.tflite" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "WHlXL1x_Z3tc" + }, + "source": [ + "## Test .tflite model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "WcE6OwrHQJya" + }, + "outputs": [], + "source": [ + "test_image_dir = 'models/research/object_detection/test_images/ducky/test/'\n", + "test_images_np = []\n", + "for i in range(1, 50):\n", + " image_path = os.path.join(test_image_dir, 'out' + str(i) + '.jpg')\n", + " test_images_np.append(np.expand_dims(\n", + " load_image_into_numpy_array(image_path), axis=0))\n", + "\n", + "# Again, uncomment this decorator if you want to run inference eagerly\n", + "def detect(interpreter, input_tensor):\n", + " \"\"\"Run detection on an input image.\n", + "\n", + " Args:\n", + " interpreter: tf.lite.Interpreter\n", + " input_tensor: A [1, height, width, 3] Tensor of type tf.float32.\n", + " Note that height and width can be anything since the image will be\n", + " immediately resized according to the needs of the model within this\n", + " function.\n", + "\n", + " Returns:\n", + " A dict containing 3 Tensors (`detection_boxes`, `detection_classes`,\n", + " and `detection_scores`).\n", + " \"\"\"\n", + " input_details = interpreter.get_input_details()\n", + " output_details = interpreter.get_output_details()\n", + "\n", + " # We use the original model for pre-processing, since the TFLite model doesn't\n", + " # include pre-processing.\n", + " preprocessed_image, shapes = detection_model.preprocess(input_tensor)\n", + " interpreter.set_tensor(input_details[0]['index'], preprocessed_image.numpy())\n", + "\n", + " interpreter.invoke()\n", + "\n", + " boxes = interpreter.get_tensor(output_details[0]['index'])\n", + " classes = interpreter.get_tensor(output_details[1]['index'])\n", + " scores = interpreter.get_tensor(output_details[2]['index'])\n", + " return boxes, classes, scores\n", + "\n", + "# Load the TFLite model and allocate tensors.\n", + "interpreter = tf.lite.Interpreter(model_path=\"tflite/model.tflite\")\n", + "interpreter.allocate_tensors()\n", + "\n", + "# Note that the first frame will trigger tracing of the tf.function, which will\n", + "# take some time, after which inference should be fast.\n", + "\n", + "label_id_offset = 1\n", + "for i in range(len(test_images_np)):\n", + " input_tensor = tf.convert_to_tensor(test_images_np[i], dtype=tf.float32)\n", + " boxes, classes, scores = detect(interpreter, input_tensor)\n", + "\n", + " plot_detections(\n", + " test_images_np[i][0],\n", + " boxes[0],\n", + " classes[0].astype(np.uint32) + label_id_offset,\n", + " scores[0],\n", + " category_index, figsize=(15, 20), image_name=\"gif_frame_\" + ('%02d' % i) + \".jpg\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZkMPOSQE0x8C" + }, + "outputs": [], + "source": [ + "imageio.plugins.freeimage.download()\n", + "\n", + "anim_file = 'duckies_test.gif'\n", + "\n", + "filenames = glob.glob('gif_frame_*.jpg')\n", + "filenames = sorted(filenames)\n", + "last = -1\n", + "images = []\n", + "for filename in filenames:\n", + " image = imageio.imread(filename)\n", + " images.append(image)\n", + "\n", + "imageio.mimsave(anim_file, images, 'GIF-FI', fps=5)\n", + "\n", + "display(IPyImage(open(anim_file, 'rb').read()))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yzaHWsS58_PQ" + }, + "source": [ + "## (Optional) Download model\n", + "\n", + "This model can be run on-device with **TensorFlow Lite**. Look at [our SSD model signature](https://www.tensorflow.org/lite/models/object_detection/overview#uses_and_limitations) to understand how to interpret the model IO tensors. Our [Object Detection example](https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection) is a good starting point for integrating the model into your mobile app.\n", + "\n", + "Refer to TFLite's [inference documentation](https://www.tensorflow.org/lite/guide/inference) for more details." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "gZ6vac3RAY3j" + }, + "outputs": [], + "source": [ + "from google.colab import files\n", + "files.download('tflite/model.tflite') " + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "name": "eager_few_shot_od_training_tflite.ipynb", + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/workspace/virtuallab/object_detection/colab_tutorials/inference_from_saved_model_tf2_colab.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/inference_from_saved_model_tf2_colab.ipynb new file mode 100644 index 0000000..1e88f4c --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/inference_from_saved_model_tf2_colab.ipynb @@ -0,0 +1,313 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "inference_from_saved_model_tf2_colab.ipynb", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cT5cdSLPX0ui" + }, + "source": [ + "# Intro to Object Detection Colab\n", + "\n", + "Welcome to the object detection colab! This demo will take you through the steps of running an \"out-of-the-box\" detection model in SavedModel format on a collection of images.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vPs64QA1Zdov" + }, + "source": [ + "Imports" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "OBzb04bdNGM8", + "colab_type": "code", + "colab": {} + }, + "source": [ + "!pip install -U --pre tensorflow==\"2.2.0\"" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "NgSXyvKSNHIl", + "colab_type": "code", + "colab": {} + }, + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "# Clone the tensorflow models repository if it doesn't already exist\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "rhpPgW7TNLs6", + "colab_type": "code", + "colab": {} + }, + "source": [ + "# Install the Object Detection API\n", + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=.\n", + "cp object_detection/packages/tf2/setup.py .\n", + "python -m pip install ." + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "yn5_uV1HLvaz", + "colab": {} + }, + "source": [ + "import io\n", + "import os\n", + "import scipy.misc\n", + "import numpy as np\n", + "import six\n", + "import time\n", + "\n", + "from six import BytesIO\n", + "\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "from PIL import Image, ImageDraw, ImageFont\n", + "\n", + "import tensorflow as tf\n", + "from object_detection.utils import visualization_utils as viz_utils\n", + "\n", + "%matplotlib inline" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "-y9R0Xllefec", + "colab": {} + }, + "source": [ + "def load_image_into_numpy_array(path):\n", + " \"\"\"Load an image from file into a numpy array.\n", + "\n", + " Puts image into numpy array to feed into tensorflow graph.\n", + " Note that by convention we put it into a numpy array with shape\n", + " (height, width, channels), where channels=3 for RGB.\n", + "\n", + " Args:\n", + " path: a file path (this can be local or on colossus)\n", + "\n", + " Returns:\n", + " uint8 numpy array with shape (img_height, img_width, 3)\n", + " \"\"\"\n", + " img_data = tf.io.gfile.GFile(path, 'rb').read()\n", + " image = Image.open(BytesIO(img_data))\n", + " (im_width, im_height) = image.size\n", + " return np.array(image.getdata()).reshape(\n", + " (im_height, im_width, 3)).astype(np.uint8)\n", + "\n", + "# Load the COCO Label Map\n", + "category_index = {\n", + " 1: {'id': 1, 'name': 'person'},\n", + " 2: {'id': 2, 'name': 'bicycle'},\n", + " 3: {'id': 3, 'name': 'car'},\n", + " 4: {'id': 4, 'name': 'motorcycle'},\n", + " 5: {'id': 5, 'name': 'airplane'},\n", + " 6: {'id': 6, 'name': 'bus'},\n", + " 7: {'id': 7, 'name': 'train'},\n", + " 8: {'id': 8, 'name': 'truck'},\n", + " 9: {'id': 9, 'name': 'boat'},\n", + " 10: {'id': 10, 'name': 'traffic light'},\n", + " 11: {'id': 11, 'name': 'fire hydrant'},\n", + " 13: {'id': 13, 'name': 'stop sign'},\n", + " 14: {'id': 14, 'name': 'parking meter'},\n", + " 15: {'id': 15, 'name': 'bench'},\n", + " 16: {'id': 16, 'name': 'bird'},\n", + " 17: {'id': 17, 'name': 'cat'},\n", + " 18: {'id': 18, 'name': 'dog'},\n", + " 19: {'id': 19, 'name': 'horse'},\n", + " 20: {'id': 20, 'name': 'sheep'},\n", + " 21: {'id': 21, 'name': 'cow'},\n", + " 22: {'id': 22, 'name': 'elephant'},\n", + " 23: {'id': 23, 'name': 'bear'},\n", + " 24: {'id': 24, 'name': 'zebra'},\n", + " 25: {'id': 25, 'name': 'giraffe'},\n", + " 27: {'id': 27, 'name': 'backpack'},\n", + " 28: {'id': 28, 'name': 'umbrella'},\n", + " 31: {'id': 31, 'name': 'handbag'},\n", + " 32: {'id': 32, 'name': 'tie'},\n", + " 33: {'id': 33, 'name': 'suitcase'},\n", + " 34: {'id': 34, 'name': 'frisbee'},\n", + " 35: {'id': 35, 'name': 'skis'},\n", + " 36: {'id': 36, 'name': 'snowboard'},\n", + " 37: {'id': 37, 'name': 'sports ball'},\n", + " 38: {'id': 38, 'name': 'kite'},\n", + " 39: {'id': 39, 'name': 'baseball bat'},\n", + " 40: {'id': 40, 'name': 'baseball glove'},\n", + " 41: {'id': 41, 'name': 'skateboard'},\n", + " 42: {'id': 42, 'name': 'surfboard'},\n", + " 43: {'id': 43, 'name': 'tennis racket'},\n", + " 44: {'id': 44, 'name': 'bottle'},\n", + " 46: {'id': 46, 'name': 'wine glass'},\n", + " 47: {'id': 47, 'name': 'cup'},\n", + " 48: {'id': 48, 'name': 'fork'},\n", + " 49: {'id': 49, 'name': 'knife'},\n", + " 50: {'id': 50, 'name': 'spoon'},\n", + " 51: {'id': 51, 'name': 'bowl'},\n", + " 52: {'id': 52, 'name': 'banana'},\n", + " 53: {'id': 53, 'name': 'apple'},\n", + " 54: {'id': 54, 'name': 'sandwich'},\n", + " 55: {'id': 55, 'name': 'orange'},\n", + " 56: {'id': 56, 'name': 'broccoli'},\n", + " 57: {'id': 57, 'name': 'carrot'},\n", + " 58: {'id': 58, 'name': 'hot dog'},\n", + " 59: {'id': 59, 'name': 'pizza'},\n", + " 60: {'id': 60, 'name': 'donut'},\n", + " 61: {'id': 61, 'name': 'cake'},\n", + " 62: {'id': 62, 'name': 'chair'},\n", + " 63: {'id': 63, 'name': 'couch'},\n", + " 64: {'id': 64, 'name': 'potted plant'},\n", + " 65: {'id': 65, 'name': 'bed'},\n", + " 67: {'id': 67, 'name': 'dining table'},\n", + " 70: {'id': 70, 'name': 'toilet'},\n", + " 72: {'id': 72, 'name': 'tv'},\n", + " 73: {'id': 73, 'name': 'laptop'},\n", + " 74: {'id': 74, 'name': 'mouse'},\n", + " 75: {'id': 75, 'name': 'remote'},\n", + " 76: {'id': 76, 'name': 'keyboard'},\n", + " 77: {'id': 77, 'name': 'cell phone'},\n", + " 78: {'id': 78, 'name': 'microwave'},\n", + " 79: {'id': 79, 'name': 'oven'},\n", + " 80: {'id': 80, 'name': 'toaster'},\n", + " 81: {'id': 81, 'name': 'sink'},\n", + " 82: {'id': 82, 'name': 'refrigerator'},\n", + " 84: {'id': 84, 'name': 'book'},\n", + " 85: {'id': 85, 'name': 'clock'},\n", + " 86: {'id': 86, 'name': 'vase'},\n", + " 87: {'id': 87, 'name': 'scissors'},\n", + " 88: {'id': 88, 'name': 'teddy bear'},\n", + " 89: {'id': 89, 'name': 'hair drier'},\n", + " 90: {'id': 90, 'name': 'toothbrush'},\n", + "}" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "QwcBC2TlPSwg", + "colab_type": "code", + "colab": {} + }, + "source": [ + "# Download the saved model and put it into models/research/object_detection/test_data/\n", + "!wget http://download.tensorflow.org/models/object_detection/tf2/20200711/efficientdet_d5_coco17_tpu-32.tar.gz\n", + "!tar -xf efficientdet_d5_coco17_tpu-32.tar.gz\n", + "!mv efficientdet_d5_coco17_tpu-32/ models/research/object_detection/test_data/" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "Z2p-PmKLYCVU", + "colab": {} + }, + "source": [ + "start_time = time.time()\n", + "tf.keras.backend.clear_session()\n", + "detect_fn = tf.saved_model.load('models/research/object_detection/test_data/efficientdet_d5_coco17_tpu-32/saved_model/')\n", + "end_time = time.time()\n", + "elapsed_time = end_time - start_time\n", + "print('Elapsed time: ' + str(elapsed_time) + 's')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "vukkhd5-9NSL", + "colab": {} + }, + "source": [ + "import time\n", + "\n", + "image_dir = 'models/research/object_detection/test_images'\n", + "\n", + "elapsed = []\n", + "for i in range(2):\n", + " image_path = os.path.join(image_dir, 'image' + str(i + 1) + '.jpg')\n", + " image_np = load_image_into_numpy_array(image_path)\n", + " input_tensor = np.expand_dims(image_np, 0)\n", + " start_time = time.time()\n", + " detections = detect_fn(input_tensor)\n", + " end_time = time.time()\n", + " elapsed.append(end_time - start_time)\n", + "\n", + " plt.rcParams['figure.figsize'] = [42, 21]\n", + " label_id_offset = 1\n", + " image_np_with_detections = image_np.copy()\n", + " viz_utils.visualize_boxes_and_labels_on_image_array(\n", + " image_np_with_detections,\n", + " detections['detection_boxes'][0].numpy(),\n", + " detections['detection_classes'][0].numpy().astype(np.int32),\n", + " detections['detection_scores'][0].numpy(),\n", + " category_index,\n", + " use_normalized_coordinates=True,\n", + " max_boxes_to_draw=200,\n", + " min_score_thresh=.40,\n", + " agnostic_mode=False)\n", + " plt.subplot(2, 1, i+1)\n", + " plt.imshow(image_np_with_detections)\n", + "\n", + "mean_elapsed = sum(elapsed) / float(len(elapsed))\n", + "print('Elapsed time: ' + str(mean_elapsed) + ' second per image')" + ], + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/workspace/virtuallab/object_detection/colab_tutorials/inference_tf2_colab.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/inference_tf2_colab.ipynb new file mode 100644 index 0000000..6b5cfaa --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/inference_tf2_colab.ipynb @@ -0,0 +1,470 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "rOvvWAVTkMR7" + }, + "source": [ + "# Intro to Object Detection Colab\n", + "\n", + "Welcome to the object detection colab! This demo will take you through the steps of running an \"out-of-the-box\" detection model on a collection of images." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "vPs64QA1Zdov" + }, + "source": [ + "## Imports and Setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "LBZ9VWZZFUCT" + }, + "outputs": [], + "source": [ + "!pip install -U --pre tensorflow==\"2.2.0\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oi28cqGGFWnY" + }, + "outputs": [], + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "# Clone the tensorflow models repository if it doesn't already exist\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "NwdsBdGhFanc" + }, + "outputs": [], + "source": [ + "# Install the Object Detection API\n", + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=.\n", + "cp object_detection/packages/tf2/setup.py .\n", + "python -m pip install ." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "yn5_uV1HLvaz" + }, + "outputs": [], + "source": [ + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "\n", + "import io\n", + "import scipy.misc\n", + "import numpy as np\n", + "from six import BytesIO\n", + "from PIL import Image, ImageDraw, ImageFont\n", + "\n", + "import tensorflow as tf\n", + "\n", + "from object_detection.utils import label_map_util\n", + "from object_detection.utils import config_util\n", + "from object_detection.utils import visualization_utils as viz_utils\n", + "from object_detection.builders import model_builder\n", + "\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "IogyryF2lFBL" + }, + "source": [ + "## Utilities" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "-y9R0Xllefec" + }, + "outputs": [], + "source": [ + "def load_image_into_numpy_array(path):\n", + " \"\"\"Load an image from file into a numpy array.\n", + "\n", + " Puts image into numpy array to feed into tensorflow graph.\n", + " Note that by convention we put it into a numpy array with shape\n", + " (height, width, channels), where channels=3 for RGB.\n", + "\n", + " Args:\n", + " path: the file path to the image\n", + "\n", + " Returns:\n", + " uint8 numpy array with shape (img_height, img_width, 3)\n", + " \"\"\"\n", + " img_data = tf.io.gfile.GFile(path, 'rb').read()\n", + " image = Image.open(BytesIO(img_data))\n", + " (im_width, im_height) = image.size\n", + " return np.array(image.getdata()).reshape(\n", + " (im_height, im_width, 3)).astype(np.uint8)\n", + "\n", + "def get_keypoint_tuples(eval_config):\n", + " \"\"\"Return a tuple list of keypoint edges from the eval config.\n", + " \n", + " Args:\n", + " eval_config: an eval config containing the keypoint edges\n", + " \n", + " Returns:\n", + " a list of edge tuples, each in the format (start, end)\n", + " \"\"\"\n", + " tuple_list = []\n", + " kp_list = eval_config.keypoint_edge\n", + " for edge in kp_list:\n", + " tuple_list.append((edge.start, edge.end))\n", + " return tuple_list" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "R4YjnOjME1gy" + }, + "outputs": [], + "source": [ + "# @title Choose the model to use, then evaluate the cell.\n", + "MODELS = {'centernet_with_keypoints': 'centernet_hg104_512x512_kpts_coco17_tpu-32', 'centernet_without_keypoints': 'centernet_hg104_512x512_coco17_tpu-8'}\n", + "\n", + "model_display_name = 'centernet_with_keypoints' # @param ['centernet_with_keypoints', 'centernet_without_keypoints']\n", + "model_name = MODELS[model_display_name]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "6917xnUSlp9x" + }, + "source": [ + "### Build a detection model and load pre-trained model weights\n", + "\n", + "This sometimes takes a little while, please be patient!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ctPavqlyPuU_" + }, + "outputs": [], + "source": [ + "# Download the checkpoint and put it into models/research/object_detection/test_data/\n", + "\n", + "if model_display_name == 'centernet_with_keypoints':\n", + " !wget http://download.tensorflow.org/models/object_detection/tf2/20200711/centernet_hg104_512x512_kpts_coco17_tpu-32.tar.gz\n", + " !tar -xf centernet_hg104_512x512_kpts_coco17_tpu-32.tar.gz\n", + " !mv centernet_hg104_512x512_kpts_coco17_tpu-32/checkpoint models/research/object_detection/test_data/\n", + "else:\n", + " !wget http://download.tensorflow.org/models/object_detection/tf2/20200711/centernet_hg104_512x512_coco17_tpu-8.tar.gz\n", + " !tar -xf centernet_hg104_512x512_coco17_tpu-8.tar.gz\n", + " !mv centernet_hg104_512x512_coco17_tpu-8/checkpoint models/research/object_detection/test_data/" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4cni4SSocvP_" + }, + "outputs": [], + "source": [ + "pipeline_config = os.path.join('models/research/object_detection/configs/tf2/',\n", + " model_name + '.config')\n", + "model_dir = 'models/research/object_detection/test_data/checkpoint/'\n", + "\n", + "# Load pipeline config and build a detection model\n", + "configs = config_util.get_configs_from_pipeline_file(pipeline_config)\n", + "model_config = configs['model']\n", + "detection_model = model_builder.build(\n", + " model_config=model_config, is_training=False)\n", + "\n", + "# Restore checkpoint\n", + "ckpt = tf.compat.v2.train.Checkpoint(\n", + " model=detection_model)\n", + "ckpt.restore(os.path.join(model_dir, 'ckpt-0')).expect_partial()\n", + "\n", + "def get_model_detection_function(model):\n", + " \"\"\"Get a tf.function for detection.\"\"\"\n", + "\n", + " @tf.function\n", + " def detect_fn(image):\n", + " \"\"\"Detect objects in image.\"\"\"\n", + "\n", + " image, shapes = model.preprocess(image)\n", + " prediction_dict = model.predict(image, shapes)\n", + " detections = model.postprocess(prediction_dict, shapes)\n", + "\n", + " return detections, prediction_dict, tf.reshape(shapes, [-1])\n", + "\n", + " return detect_fn\n", + "\n", + "detect_fn = get_model_detection_function(detection_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "NKtD0IeclbL5" + }, + "source": [ + "# Load label map data (for plotting).\n", + "\n", + "Label maps correspond index numbers to category names, so that when our convolution network predicts `5`, we know that this corresponds to `airplane`. Here we use internal utility functions, but anything that returns a dictionary mapping integers to appropriate string labels would be fine." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "5mucYUS6exUJ" + }, + "outputs": [], + "source": [ + "label_map_path = configs['eval_input_config'].label_map_path\n", + "label_map = label_map_util.load_labelmap(label_map_path)\n", + "categories = label_map_util.convert_label_map_to_categories(\n", + " label_map,\n", + " max_num_classes=label_map_util.get_max_label_map_index(label_map),\n", + " use_display_name=True)\n", + "category_index = label_map_util.create_category_index(categories)\n", + "label_map_dict = label_map_util.get_label_map_dict(label_map, use_display_name=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RLusV1o-mAx8" + }, + "source": [ + "### Putting everything together!\n", + "\n", + "Run the below code which loads an image, runs it through the detection model and visualizes the detection results, including the keypoints.\n", + "\n", + "Note that this will take a long time (several minutes) the first time you run this code due to tf.function's trace-compilation --- on subsequent runs (e.g. on new images), things will be faster.\n", + "\n", + "Here are some simple things to try out if you are curious:\n", + "* Try running inference on your own images (local paths work)\n", + "* Modify some of the input images and see if detection still works. Some simple things to try out here (just uncomment the relevant portions of code) include flipping the image horizontally, or converting to grayscale (note that we still expect the input image to have 3 channels).\n", + "* Print out `detections['detection_boxes']` and try to match the box locations to the boxes in the image. Notice that coordinates are given in normalized form (i.e., in the interval [0, 1]).\n", + "* Set min_score_thresh to other values (between 0 and 1) to allow more detections in or to filter out more detections.\n", + "\n", + "Note that you can run this cell repeatedly without rerunning earlier cells.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "vr_Fux-gfaG9" + }, + "outputs": [], + "source": [ + "image_dir = 'models/research/object_detection/test_images/'\n", + "image_path = os.path.join(image_dir, 'image2.jpg')\n", + "image_np = load_image_into_numpy_array(image_path)\n", + "\n", + "# Things to try:\n", + "# Flip horizontally\n", + "# image_np = np.fliplr(image_np).copy()\n", + "\n", + "# Convert image to grayscale\n", + "# image_np = np.tile(\n", + "# np.mean(image_np, 2, keepdims=True), (1, 1, 3)).astype(np.uint8)\n", + "\n", + "input_tensor = tf.convert_to_tensor(\n", + " np.expand_dims(image_np, 0), dtype=tf.float32)\n", + "detections, predictions_dict, shapes = detect_fn(input_tensor)\n", + "\n", + "label_id_offset = 1\n", + "image_np_with_detections = image_np.copy()\n", + "\n", + "# Use keypoints if available in detections\n", + "keypoints, keypoint_scores = None, None\n", + "if 'detection_keypoints' in detections:\n", + " keypoints = detections['detection_keypoints'][0].numpy()\n", + " keypoint_scores = detections['detection_keypoint_scores'][0].numpy()\n", + "\n", + "viz_utils.visualize_boxes_and_labels_on_image_array(\n", + " image_np_with_detections,\n", + " detections['detection_boxes'][0].numpy(),\n", + " (detections['detection_classes'][0].numpy() + label_id_offset).astype(int),\n", + " detections['detection_scores'][0].numpy(),\n", + " category_index,\n", + " use_normalized_coordinates=True,\n", + " max_boxes_to_draw=200,\n", + " min_score_thresh=.30,\n", + " agnostic_mode=False,\n", + " keypoints=keypoints,\n", + " keypoint_scores=keypoint_scores,\n", + " keypoint_edges=get_keypoint_tuples(configs['eval_config']))\n", + "\n", + "plt.figure(figsize=(12,16))\n", + "plt.imshow(image_np_with_detections)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "lYnOxprty3TD" + }, + "source": [ + "## Digging into the model's intermediate predictions\n", + "\n", + "For this part we will assume that the detection model is a CenterNet model following Zhou et al (https://arxiv.org/abs/1904.07850). And more specifically, we will assume that `detection_model` is of type `meta_architectures.center_net_meta_arch.CenterNetMetaArch`.\n", + "\n", + "As one of its intermediate predictions, CenterNet produces a heatmap of box centers for each class (for example, it will produce a heatmap whose size is proportional to that of the image that lights up at the center of each, e.g., \"zebra\"). In the following, we will visualize these intermediate class center heatmap predictions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "xBgYgSGMhHVi" + }, + "outputs": [], + "source": [ + "if detection_model.__class__.__name__ != 'CenterNetMetaArch':\n", + " raise AssertionError('The meta-architecture for this section '\n", + " 'is assumed to be CenterNetMetaArch!')\n", + "\n", + "def get_heatmap(predictions_dict, class_name):\n", + " \"\"\"Grabs class center logits and apply inverse logit transform.\n", + "\n", + " Args:\n", + " predictions_dict: dictionary of tensors containing a `object_center`\n", + " field of shape [1, heatmap_width, heatmap_height, num_classes]\n", + " class_name: string name of category (e.g., `horse`)\n", + "\n", + " Returns:\n", + " heatmap: 2d Tensor heatmap representing heatmap of centers for a given class\n", + " (For CenterNet, this is 128x128 or 256x256) with values in [0,1]\n", + " \"\"\"\n", + " class_index = label_map_dict[class_name]\n", + " class_center_logits = predictions_dict['object_center'][0]\n", + " class_center_logits = class_center_logits[0][\n", + " :, :, class_index - label_id_offset]\n", + " heatmap = tf.exp(class_center_logits) / (tf.exp(class_center_logits) + 1)\n", + " return heatmap\n", + "\n", + "def unpad_heatmap(heatmap, image_np):\n", + " \"\"\"Reshapes/unpads heatmap appropriately.\n", + "\n", + " Reshapes/unpads heatmap appropriately to match image_np.\n", + "\n", + " Args:\n", + " heatmap: Output of `get_heatmap`, a 2d Tensor\n", + " image_np: uint8 numpy array with shape (img_height, img_width, 3). Note\n", + " that due to padding, the relationship between img_height and img_width\n", + " might not be a simple scaling.\n", + "\n", + " Returns:\n", + " resized_heatmap_unpadded: a resized heatmap (2d Tensor) that is the same\n", + " size as `image_np`\n", + " \"\"\"\n", + " heatmap = tf.tile(tf.expand_dims(heatmap, 2), [1, 1, 3]) * 255\n", + " pre_strided_size = detection_model._stride * heatmap.shape[0]\n", + " resized_heatmap = tf.image.resize(\n", + " heatmap, [pre_strided_size, pre_strided_size],\n", + " method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)\n", + " resized_heatmap_unpadded = tf.slice(resized_heatmap, begin=[0,0,0], size=shapes)\n", + " return tf.image.resize(\n", + " resized_heatmap_unpadded,\n", + " [image_np.shape[0], image_np.shape[1]],\n", + " method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)[:,:,0]\n", + "\n", + "\n", + "class_name = 'kite'\n", + "heatmap = get_heatmap(predictions_dict, class_name)\n", + "resized_heatmap_unpadded = unpad_heatmap(heatmap, image_np)\n", + "plt.figure(figsize=(12,16))\n", + "plt.imshow(image_np_with_detections)\n", + "plt.imshow(resized_heatmap_unpadded, alpha=0.7,vmin=0, vmax=160, cmap='viridis')\n", + "plt.title('Object center heatmap (class: ' + class_name + ')')\n", + "plt.show()\n", + "\n", + "class_name = 'person'\n", + "heatmap = get_heatmap(predictions_dict, class_name)\n", + "resized_heatmap_unpadded = unpad_heatmap(heatmap, image_np)\n", + "plt.figure(figsize=(12,16))\n", + "plt.imshow(image_np_with_detections)\n", + "plt.imshow(resized_heatmap_unpadded, alpha=0.7,vmin=0, vmax=160, cmap='viridis')\n", + "plt.title('Object center heatmap (class: ' + class_name + ')')\n", + "plt.show()" + ] + } + ], + "metadata": { + "colab": { + "collapsed_sections": [], + "name": "inference_tf2_colab.ipynb", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/workspace/virtuallab/object_detection/colab_tutorials/object_detection_tutorial.ipynb b/workspace/virtuallab/object_detection/colab_tutorials/object_detection_tutorial.ipynb new file mode 100644 index 0000000..2185a1c --- /dev/null +++ b/workspace/virtuallab/object_detection/colab_tutorials/object_detection_tutorial.ipynb @@ -0,0 +1,847 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "V8-yl-s-WKMG" + }, + "source": [ + "# Object Detection API Demo\n", + "\n", + "
\n", + " \n", + " Run in Google Colab\n", + " \n", + "\n", + " \n", + " View source on GitHub\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "3cIrseUv6WKz" + }, + "source": [ + "Welcome to the [Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection). This notebook will walk you step by step through the process of using a pre-trained model to detect objects in an image." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "VrJaG0cYN9yh" + }, + "source": [ + "> **Important**: This tutorial is to help you through the first step towards using [Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection) to build models. If you just just need an off the shelf model that does the job, see the [TFHub object detection example](https://colab.sandbox.google.com/github/tensorflow/hub/blob/master/examples/colab/object_detection.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "kFSqkTCdWKMI" + }, + "source": [ + "# Setup" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "awjrpqy-6MaQ" + }, + "source": [ + "Important: If you're running on a local machine, be sure to follow the [installation instructions](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md). This notebook includes only what's necessary to run in Colab." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "p3UGXxUii5Ym" + }, + "source": [ + "### Install" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hGL97-GXjSUw" + }, + "outputs": [], + "source": [ + "!pip install -U --pre tensorflow==\"2.*\"\n", + "!pip install tf_slim" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "n_ap_s9ajTHH" + }, + "source": [ + "Make sure you have `pycocotools` installed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "Bg8ZyA47i3pY" + }, + "outputs": [], + "source": [ + "!pip install pycocotools" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "-vsOL3QR6kqs" + }, + "source": [ + "Get `tensorflow/models` or `cd` to parent directory of the repository." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ykA0c-om51s1" + }, + "outputs": [], + "source": [ + "import os\n", + "import pathlib\n", + "\n", + "\n", + "if \"models\" in pathlib.Path.cwd().parts:\n", + " while \"models\" in pathlib.Path.cwd().parts:\n", + " os.chdir('..')\n", + "elif not pathlib.Path('models').exists():\n", + " !git clone --depth 1 https://github.com/tensorflow/models" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "O219m6yWAj9l" + }, + "source": [ + "Compile protobufs and install the object_detection package" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "PY41vdYYNlXc" + }, + "outputs": [], + "source": [ + "%%bash\n", + "cd models/research/\n", + "protoc object_detection/protos/*.proto --python_out=." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "s62yJyQUcYbp" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing /home/job/models/research\n", + "Collecting Pillow>=1.0 (from object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/12/ad/61f8dfba88c4e56196bf6d056cdbba64dc9c5dfdfbc97d02e6472feed913/Pillow-6.2.2-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting Matplotlib>=2.1 (from object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/9d/40/5ba7d4a3f80d39d409f21899972596bf62c8606f1406a825029649eaa439/matplotlib-2.2.5-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting Cython>=0.28.1 (from object-detection==0.1)\n", + " Downloading https://files.pythonhosted.org/packages/59/c1/0b69d125ab9819869cffff2f416158acf2684bdb4bf54eccf887717e2cbd/Cython-0.29.21-cp27-cp27mu-manylinux1_x86_64.whl (1.9MB)\n", + "Collecting cycler>=0.10 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/f7/d2/e07d3ebb2bd7af696440ce7e754c59dd546ffe1bbe732c8ab68b9c834e61/cycler-0.10.0-py2.py3-none-any.whl\n", + "Collecting numpy>=1.7.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/3a/5f/47e578b3ae79e2624e205445ab77a1848acdaa2929a00eeef6b16eaaeb20/numpy-1.16.6-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting backports.functools-lru-cache (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/da/d1/080d2bb13773803648281a49e3918f65b31b7beebf009887a529357fd44a/backports.functools_lru_cache-1.6.1-py2.py3-none-any.whl\n", + "Collecting subprocess32 (from Matplotlib>=2.1->object-detection==0.1)\n", + "Collecting kiwisolver>=1.0.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/3d/78/cb9248b2289ec31e301137cedbe4ca503a74ca87f88cdbfd2f8be52323bf/kiwisolver-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl\n", + "Collecting pytz (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/4f/a4/879454d49688e2fad93e59d7d4efda580b783c745fd2ec2a3adf87b0808d/pytz-2020.1-py2.py3-none-any.whl\n", + "Collecting six>=1.10 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl\n", + "Collecting python-dateutil>=2.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl\n", + "Collecting pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 (from Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/8a/bb/488841f56197b13700afd5658fc279a2025a39e22449b7cf29864669b15d/pyparsing-2.4.7-py2.py3-none-any.whl\n", + "Collecting setuptools (from kiwisolver>=1.0.1->Matplotlib>=2.1->object-detection==0.1)\n", + " Using cached https://files.pythonhosted.org/packages/e1/b7/182161210a13158cd3ccc41ee19aadef54496b74f2817cc147006ec932b4/setuptools-44.1.1-py2.py3-none-any.whl\n", + "Installing collected packages: Pillow, six, cycler, numpy, backports.functools-lru-cache, subprocess32, setuptools, kiwisolver, pytz, python-dateutil, pyparsing, Matplotlib, Cython, object-detection\n", + " Running setup.py install for object-detection: started\n", + " Running setup.py install for object-detection: finished with status 'done'\n", + "Successfully installed Cython-0.29.21 Matplotlib-2.2.5 Pillow-6.2.2 backports.functools-lru-cache-1.6.1 cycler-0.10.0 kiwisolver-1.1.0 numpy-1.16.6 object-detection-0.1 pyparsing-2.4.7 python-dateutil-2.8.1 pytz-2020.1 setuptools-44.1.1 six-1.15.0 subprocess32-3.5.4\n" + ] + } + ], + "source": [ + "%%bash \n", + "cd models/research\n", + "pip install ." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "LBdjK2G5ywuc" + }, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hV4P5gyTWKMI" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:516: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:517: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:518: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:519: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:520: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorflow/python/framework/dtypes.py:525: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:541: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint8 = np.dtype([(\"qint8\", np.int8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:542: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint8 = np.dtype([(\"quint8\", np.uint8, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:543: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint16 = np.dtype([(\"qint16\", np.int16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:544: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_quint16 = np.dtype([(\"quint16\", np.uint16, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:545: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " _np_qint32 = np.dtype([(\"qint32\", np.int32, 1)])\n", + "/home/job/.local/lib/python3.6/site-packages/tensorboard/compat/tensorflow_stub/dtypes.py:550: FutureWarning: Passing (type, 1) or '1type' as a synonym of type is deprecated; in a future version of numpy, it will be understood as (type, (1,)) / '(1,)type'.\n", + " np_resource = np.dtype([(\"resource\", np.ubyte, 1)])\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import os\n", + "import six.moves.urllib as urllib\n", + "import sys\n", + "import tarfile\n", + "import tensorflow as tf\n", + "import zipfile\n", + "\n", + "from collections import defaultdict\n", + "from io import StringIO\n", + "from matplotlib import pyplot as plt\n", + "from PIL import Image\n", + "from IPython.display import display" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "r5FNuiRPWKMN" + }, + "source": [ + "Import the object detection module." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "4-IMl4b6BdGO" + }, + "outputs": [], + "source": [ + "from object_detection.utils import ops as utils_ops\n", + "from object_detection.utils import label_map_util\n", + "from object_detection.utils import visualization_utils as vis_util" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "RYPCiag2iz_q" + }, + "source": [ + "Patches:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "mF-YlMl8c_bM" + }, + "outputs": [], + "source": [ + "# patch tf1 into `utils.ops`\n", + "utils_ops.tf = tf.compat.v1\n", + "\n", + "# Patch the location of gfile\n", + "tf.gfile = tf.io.gfile" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "cfn_tRFOWKMO" + }, + "source": [ + "# Model preparation " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "X_sEBLpVWKMQ" + }, + "source": [ + "## Variables\n", + "\n", + "Any model exported using the `export_inference_graph.py` tool can be loaded here simply by changing the path.\n", + "\n", + "By default we use an \"SSD with Mobilenet\" model here. See the [detection model zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md) for a list of other models that can be run out-of-the-box with varying speeds and accuracies." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "7ai8pLZZWKMS" + }, + "source": [ + "## Loader" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "zm8xp-0eoItE" + }, + "outputs": [], + "source": [ + "def load_model(model_name):\n", + "\n", + " model_dir = pathlib.Path('/home/job/models/inference_graph')/\"saved_model\"\n", + "\n", + " model = tf.compat.v2.saved_model.load(str(model_dir))\n", + "\n", + " return model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "_1MVVTcLWKMW" + }, + "source": [ + "## Loading label map\n", + "Label maps map indices to category names, so that when our convolution network predicts `5`, we know that this corresponds to `airplane`. Here we use internal utility functions, but anything that returns a dictionary mapping integers to appropriate string labels would be fine" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "hDbpHkiWWKMX" + }, + "outputs": [], + "source": [ + "# List of the strings that is used to add correct label for each box.\n", + "PATH_TO_LABELS = 'models/annotations/label_map.pbtxt'\n", + "category_index = label_map_util.create_category_index_from_labelmap(PATH_TO_LABELS, use_display_name=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "oVU3U_J6IJVb" + }, + "source": [ + "For the sake of simplicity we will test on 2 images:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "jG-zn5ykWKMd" + }, + "outputs": [], + "source": [ + "# If you want to test the code with your images, just add path to the images to the TEST_IMAGE_PATHS.\n", + "#PATH_TO_TEST_IMAGES_DIR = pathlib.Path('models/images/test')\n", + "#TEST_IMAGE_PATHS = sorted(list(PATH_TO_TEST_IMAGES_DIR.glob(\"*.jpg\")))\n", + "#TEST_IMAGE_PATHS" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "H0_1AGhrWKMc" + }, + "source": [ + "# Detection" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "f7aOtOlebK7h" + }, + "source": [ + "Load an object detection model:" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "1XNT0wxybKR6" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO:tensorflow:Saver not created because there are no variables in the graph to restore\n" + ] + } + ], + "source": [ + "model_name = 'ssd_mobilenet_v1_coco_2018_01_28'\n", + "detection_model = load_model(model_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "yN1AYfAEJIGp" + }, + "source": [ + "Check the model's input signature, it expects a batch of 3-color images of type uint8:" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CK4cnry6wsHY" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[]\n" + ] + } + ], + "source": [ + "print(detection_model.signatures['serving_default'].inputs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "Q8u3BjpMJXZF" + }, + "source": [ + "And returns several outputs:" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "oLSZpfaYwuSk" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'detection_classes': tf.float32,\n", + " 'detection_scores': tf.float32,\n", + " 'raw_detection_scores': tf.float32,\n", + " 'raw_detection_boxes': tf.float32,\n", + " 'detection_boxes': tf.float32,\n", + " 'num_detections': tf.float32}" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "detection_model.signatures['serving_default'].output_dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "FZyKUJeuxvpT" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'detection_classes': TensorShape([Dimension(None), Dimension(100)]),\n", + " 'detection_scores': TensorShape([Dimension(None), Dimension(100)]),\n", + " 'raw_detection_scores': TensorShape([Dimension(None), Dimension(None), Dimension(3)]),\n", + " 'raw_detection_boxes': TensorShape([Dimension(None), Dimension(None), Dimension(4)]),\n", + " 'detection_boxes': TensorShape([Dimension(None), Dimension(100), Dimension(4)]),\n", + " 'num_detections': TensorShape([Dimension(None)])}" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "detection_model.signatures['serving_default'].output_shapes" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "JP5qZ7sXJpwG" + }, + "source": [ + "Add a wrapper function to call the model, and cleanup the outputs:" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "ajmR_exWyN76" + }, + "outputs": [], + "source": [ + "def run_inference_for_single_image(model, image):\n", + " image = np.asarray(image)\n", + " # The input needs to be a tensor, convert it using `tf.convert_to_tensor`.\n", + " input_tensor = tf.convert_to_tensor(image)\n", + " # The model expects a batch of images, so add an axis with `tf.newaxis`.\n", + " input_tensor = input_tensor[tf.newaxis,...]\n", + "\n", + " # Run inference\n", + " model_fn = model.signatures['serving_default']\n", + " output_dict = model_fn(input_tensor)\n", + "\n", + " # All outputs are batches tensors.\n", + " # Convert to numpy arrays, and take index [0] to remove the batch dimension.\n", + " # We're only interested in the first num_detections.\n", + " num_detections = int(output_dict.pop(1))\n", + " output_dict = {key:value[0, :num_detections].numpy() \n", + " for key,value in output_dict.items()}\n", + " output_dict['num_detections'] = num_detections\n", + "\n", + " # detection_classes should be ints.\n", + " output_dict['detection_classes'] = output_dict['detection_classes'].astype(np.int64)\n", + " \n", + " # Handle models with masks:\n", + " if 'detection_masks' in output_dict:\n", + " # Reframe the the bbox mask to the image size.\n", + " detection_masks_reframed = utils_ops.reframe_box_masks_to_image_masks(\n", + " output_dict['detection_masks'], output_dict['detection_boxes'],\n", + " image.shape[0], image.shape[1]) \n", + " detection_masks_reframed = tf.cast(detection_masks_reframed > 0.5,\n", + " tf.uint8)\n", + " output_dict['detection_masks_reframed'] = detection_masks_reframed.numpy()\n", + " \n", + " return output_dict" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "z1wq0LVyMRR_" + }, + "source": [ + "Run it on each test image and show the results:" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "DWh_1zz6aqxs" + }, + "outputs": [], + "source": [ + "def show_inference(model, image_path):\n", + " # the array based representation of the image will be used later in order to prepare the\n", + " # result image with boxes and labels on it.\n", + " image_np = np.array(Image.open(image_path))\n", + " # Actual detection.\n", + " output_dict = run_inference_for_single_image(model, image_np)\n", + " # Visualization of the results of a detection.\n", + " vis_util.visualize_boxes_and_labels_on_image_array(\n", + " image_np,\n", + " output_dict['detection_boxes'],\n", + " output_dict['detection_classes'],\n", + " output_dict['detection_scores'],\n", + " category_index,\n", + " instance_masks=output_dict.get('detection_masks_reframed', None),\n", + " use_normalized_coordinates=True,\n", + " line_thickness=8)\n", + "\n", + " display(Image.fromarray(image_np))" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "3a5wMHN8WKMh" + }, + "outputs": [ + { + "ename": "KeyError", + "evalue": "1", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m#for image_path in TEST_IMAGE_PATHS:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mimagepath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"/home/job/models/test.jpg\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mshow_inference\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdetection_model\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimage_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mshow_inference\u001b[0;34m(model, image_path)\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mimage_np\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0marray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mImage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimage_path\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;31m# Actual detection.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 6\u001b[0;31m \u001b[0moutput_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrun_inference_for_single_image\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mimage_np\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 7\u001b[0m \u001b[0;31m# Visualization of the results of a detection.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 8\u001b[0m vis_util.visualize_boxes_and_labels_on_image_array(\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mrun_inference_for_single_image\u001b[0;34m(model, image)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;31m# Convert to numpy arrays, and take index [0] to remove the batch dimension.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;31m# We're only interested in the first num_detections.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0mnum_detections\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutput_dict\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpop\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m output_dict = {key:value[0, :num_detections].numpy() \n\u001b[1;32m 17\u001b[0m for key,value in output_dict.items()}\n", + "\u001b[0;31mKeyError\u001b[0m: 1" + ] + } + ], + "source": [ + "#for image_path in TEST_IMAGE_PATHS:\n", + "imagepath = \"/home/job/models/test.jpg\"\n", + "show_inference(detection_model, image_path)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "DsspMPX3Cssg" + }, + "source": [ + "## Instance Segmentation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "CzkVv_n2MxKC" + }, + "outputs": [], + "source": [ + "model_name = \"mask_rcnn_inception_resnet_v2_atrous_coco_2018_01_28\"\n", + "masking_model = load_model(model_name)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "0S7aZi8ZOhVV" + }, + "source": [ + "The instance segmentation model includes a `detection_masks` output:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "vQ2Sj2VIOZLA" + }, + "outputs": [], + "source": [ + "masking_model.output_shapes" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": {}, + "colab_type": "code", + "id": "AS57rZlnNL7W" + }, + "outputs": [], + "source": [ + "for image_path in TEST_IMAGE_PATHS:\n", + " show_inference(masking_model, image_path)" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "last_runtime": { + "build_target": "//learning/brain/python/client:colab_notebook", + "kind": "private" + }, + "name": "object_detection_tutorial.ipynb", + "private_outputs": true, + "provenance": [ + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/colab_tutorials/object_detection_tutorial.ipynb", + "timestamp": 1594335690840 + }, + { + "file_id": "1LNYL6Zsn9Xlil2CVNOTsgDZQSBKeOjCh", + "timestamp": 1566498233247 + }, + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1566488313397 + }, + { + "file_id": "/piper/depot/google3/third_party/py/tensorflow_docs/g3doc/en/r2/tutorials/generative/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1566145894046 + }, + { + "file_id": "1nBPoWynOV0auSIy40eQcBIk9C6YRSkI8", + "timestamp": 1566145841085 + }, + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1556295408037 + }, + { + "file_id": "1layerger-51XwWOwYMY_5zHaCavCeQkO", + "timestamp": 1556214267924 + }, + { + "file_id": "/piper/depot/google3/third_party/tensorflow_models/object_detection/object_detection_tutorial.ipynb?workspaceId=markdaoust:copybara_AFABFE845DCD573AD3D43A6BAFBE77D4_0::citc", + "timestamp": 1556207836484 + }, + { + "file_id": "1w6mqQiNV3liPIX70NOgitOlDF1_4sRMw", + "timestamp": 1556154824101 + }, + { + "file_id": "https://github.com/tensorflow/models/blob/master/research/object_detection/object_detection_tutorial.ipynb", + "timestamp": 1556150293326 + } + ] + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.9" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_coco17_tpu-32.config new file mode 100644 index 0000000..c0a90ef --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_coco17_tpu-32.config @@ -0,0 +1,129 @@ +# CenterNet meta-architecture from the "Objects as Points" [2] paper with the +# hourglass[1] backbone. +# [1]: https://arxiv.org/abs/1603.06937 +# [2]: https://arxiv.org/abs/1904.07850 +# Trained on COCO, initialized from Extremenet Detection checkpoint +# Train on TPU-32 v3 +# +# Achieves 44.6 mAP on COCO17 Val + + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "hourglass_104" + bgr_ordering: true + channel_means: [104.01362025, 114.03422265, 119.9165958 ] + channel_stds: [73.6027665 , 69.89082075, 70.9150767 ] + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 1024 + max_dimension: 1024 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.1 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + min_box_overlap_iou: 0.7 + max_box_predictions: 100 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + } +} + +train_config: { + + batch_size: 128 + num_steps: 50000 + + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_adjust_brightness { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } + + optimizer { + adam_optimizer: { + epsilon: 1e-7 # Match tf.keras.optimizers.Adam's default. + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 1e-3 + total_steps: 50000 + warmup_learning_rate: 2.5e-4 + warmup_steps: 5000 + } + } + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-1" + fine_tune_checkpoint_type: "detection" +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_kpts_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_kpts_coco17_tpu-32.config new file mode 100644 index 0000000..da7136f --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_1024x1024_kpts_coco17_tpu-32.config @@ -0,0 +1,374 @@ +# CenterNet meta-architecture from the "Objects as Points" [2] paper with the +# hourglass[1] backbone. This config achieves an mAP of 42.8/64.5 +/- 0.16 on +# COCO 17 (averaged over 5 runs). This config is TPU compatible. +# [1]: https://arxiv.org/abs/1603.06937 +# [2]: https://arxiv.org/abs/1904.07850 + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "hourglass_104" + channel_means: 104.01361846923828 + channel_means: 114.03422546386719 + channel_means: 119.91659545898438 + channel_stds: 73.60276794433594 + channel_stds: 69.89082336425781 + channel_stds: 70.91507720947266 + bgr_ordering: true + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 1024 + max_dimension: 1024 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.10000000149011612 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + min_box_overlap_iou: 0.699999988079071 + max_box_predictions: 100 + } + keypoint_label_map_path: "PATH_TO_BE_CONFIGURED" + keypoint_estimation_task { + task_name: "human_pose" + task_loss_weight: 1.0 + loss { + localization_loss { + l1_localization_loss { + } + } + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + keypoint_class_name: "/m/01g317" + keypoint_label_to_std { + key: "left_ankle" + value: 0.8899999856948853 + } + keypoint_label_to_std { + key: "left_ear" + value: 0.3499999940395355 + } + keypoint_label_to_std { + key: "left_elbow" + value: 0.7200000286102295 + } + keypoint_label_to_std { + key: "left_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "left_hip" + value: 1.0700000524520874 + } + keypoint_label_to_std { + key: "left_knee" + value: 0.8899999856948853 + } + keypoint_label_to_std { + key: "left_shoulder" + value: 0.7900000214576721 + } + keypoint_label_to_std { + key: "left_wrist" + value: 0.6200000047683716 + } + keypoint_label_to_std { + key: "nose" + value: 0.25999999046325684 + } + keypoint_label_to_std { + key: "right_ankle" + value: 0.8899999856948853 + } + keypoint_label_to_std { + key: "right_ear" + value: 0.3499999940395355 + } + keypoint_label_to_std { + key: "right_elbow" + value: 0.7200000286102295 + } + keypoint_label_to_std { + key: "right_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "right_hip" + value: 1.0700000524520874 + } + keypoint_label_to_std { + key: "right_knee" + value: 0.8899999856948853 + } + keypoint_label_to_std { + key: "right_shoulder" + value: 0.7900000214576721 + } + keypoint_label_to_std { + key: "right_wrist" + value: 0.6200000047683716 + } + keypoint_regression_loss_weight: 0.10000000149011612 + keypoint_heatmap_loss_weight: 1.0 + keypoint_offset_loss_weight: 1.0 + offset_peak_radius: 3 + per_keypoint_offset: true + } + } +} +train_config { + batch_size: 128 + data_augmentation_options { + random_horizontal_flip { + keypoint_flip_permutation: 0 + keypoint_flip_permutation: 2 + keypoint_flip_permutation: 1 + keypoint_flip_permutation: 4 + keypoint_flip_permutation: 3 + keypoint_flip_permutation: 6 + keypoint_flip_permutation: 5 + keypoint_flip_permutation: 8 + keypoint_flip_permutation: 7 + keypoint_flip_permutation: 10 + keypoint_flip_permutation: 9 + keypoint_flip_permutation: 12 + keypoint_flip_permutation: 11 + keypoint_flip_permutation: 14 + keypoint_flip_permutation: 13 + keypoint_flip_permutation: 16 + keypoint_flip_permutation: 15 + } + } + data_augmentation_options { + random_adjust_hue { + } + } + data_augmentation_options { + random_adjust_contrast { + } + } + data_augmentation_options { + random_adjust_saturation { + } + } + data_augmentation_options { + random_adjust_brightness { + } + } + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6000000238418579 + scale_max: 1.2999999523162842 + } + } + optimizer { + adam_optimizer { + learning_rate { + cosine_decay_learning_rate { + learning_rate_base: 0.0010000000474974513 + total_steps: 250000 + warmup_learning_rate: 0.0002500000118743628 + warmup_steps: 5000 + } + } + epsilon: 1.0000000116860974e-07 + } + use_moving_average: false + } + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED" + num_steps: 250000 + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + fine_tune_checkpoint_type: "detection" + fine_tune_checkpoint_version: V2 +} +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + num_keypoints: 17 +} +eval_config { + num_visualizations: 10 + metrics_set: "coco_detection_metrics" + use_moving_averages: false + min_score_threshold: 0.20000000298023224 + max_num_boxes_to_visualize: 20 + batch_size: 1 + parameterized_metric { + coco_keypoint_metrics { + class_label: "person" + keypoint_label_to_sigmas { + key: "left_ankle" + value: 0.08900000154972076 + } + keypoint_label_to_sigmas { + key: "left_ear" + value: 0.03500000014901161 + } + keypoint_label_to_sigmas { + key: "left_elbow" + value: 0.07199999690055847 + } + keypoint_label_to_sigmas { + key: "left_eye" + value: 0.02500000037252903 + } + keypoint_label_to_sigmas { + key: "left_hip" + value: 0.10700000077486038 + } + keypoint_label_to_sigmas { + key: "left_knee" + value: 0.08699999749660492 + } + keypoint_label_to_sigmas { + key: "left_shoulder" + value: 0.07900000363588333 + } + keypoint_label_to_sigmas { + key: "left_wrist" + value: 0.06199999898672104 + } + keypoint_label_to_sigmas { + key: "nose" + value: 0.026000000536441803 + } + keypoint_label_to_sigmas { + key: "right_ankle" + value: 0.08900000154972076 + } + keypoint_label_to_sigmas { + key: "right_ear" + value: 0.03500000014901161 + } + keypoint_label_to_sigmas { + key: "right_elbow" + value: 0.07199999690055847 + } + keypoint_label_to_sigmas { + key: "right_eye" + value: 0.02500000037252903 + } + keypoint_label_to_sigmas { + key: "right_hip" + value: 0.10700000077486038 + } + keypoint_label_to_sigmas { + key: "right_knee" + value: 0.08699999749660492 + } + keypoint_label_to_sigmas { + key: "right_shoulder" + value: 0.07900000363588333 + } + keypoint_label_to_sigmas { + key: "right_wrist" + value: 0.06199999898672104 + } + } + } + keypoint_edge { + start: 0 + end: 1 + } + keypoint_edge { + start: 0 + end: 2 + } + keypoint_edge { + start: 1 + end: 3 + } + keypoint_edge { + start: 2 + end: 4 + } + keypoint_edge { + start: 0 + end: 5 + } + keypoint_edge { + start: 0 + end: 6 + } + keypoint_edge { + start: 5 + end: 7 + } + keypoint_edge { + start: 7 + end: 9 + } + keypoint_edge { + start: 6 + end: 8 + } + keypoint_edge { + start: 8 + end: 10 + } + keypoint_edge { + start: 5 + end: 6 + } + keypoint_edge { + start: 5 + end: 11 + } + keypoint_edge { + start: 6 + end: 12 + } + keypoint_edge { + start: 11 + end: 12 + } + keypoint_edge { + start: 11 + end: 13 + } + keypoint_edge { + start: 13 + end: 15 + } + keypoint_edge { + start: 12 + end: 14 + } + keypoint_edge { + start: 14 + end: 16 + } +} +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } + num_keypoints: 17 +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_coco17_tpu-8.config new file mode 100644 index 0000000..9e38d98 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_coco17_tpu-8.config @@ -0,0 +1,143 @@ +# CenterNet meta-architecture from the "Objects as Points" [2] paper with the +# hourglass[1] backbone. +# [1]: https://arxiv.org/abs/1603.06937 +# [2]: https://arxiv.org/abs/1904.07850 +# Trained on COCO, initialized from Extremenet Detection checkpoint +# Train on TPU-8 +# +# Achieves 41.9 mAP on COCO17 Val + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "hourglass_104" + bgr_ordering: true + channel_means: [104.01362025, 114.03422265, 119.9165958 ] + channel_stds: [73.6027665 , 69.89082075, 70.9150767 ] + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 512 + max_dimension: 512 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.1 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + min_box_overlap_iou: 0.7 + max_box_predictions: 100 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + } +} + +train_config: { + + batch_size: 128 + num_steps: 140000 + + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_crop_image { + min_aspect_ratio: 0.5 + max_aspect_ratio: 1.7 + random_coef: 0.25 + } + } + + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_adjust_brightness { + } + } + + data_augmentation_options { + random_absolute_pad_image { + max_height_padding: 200 + max_width_padding: 200 + pad_color: [0, 0, 0] + } + } + + optimizer { + adam_optimizer: { + epsilon: 1e-7 # Match tf.keras.optimizers.Adam's default. + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 1e-3 + schedule { + step: 90000 + learning_rate: 1e-4 + } + schedule { + step: 120000 + learning_rate: 1e-5 + } + } + } + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-1" + fine_tune_checkpoint_type: "detection" +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_kpts_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_kpts_coco17_tpu-32.config new file mode 100644 index 0000000..ce56528 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_hourglass104_512x512_kpts_coco17_tpu-32.config @@ -0,0 +1,395 @@ +# CenterNet meta-architecture from the "Objects as Points" [2] paper with the +# hourglass[1] backbone. This config achieves an mAP of 40.0/61.4 +/- 0.16 on +# COCO 17 (averaged over 5 runs). This config is TPU compatible. +# [1]: https://arxiv.org/abs/1603.06937 +# [2]: https://arxiv.org/abs/1904.07850 + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "hourglass_104" + bgr_ordering: true + channel_means: [104.01362025, 114.03422265, 119.9165958 ] + channel_stds: [73.6027665 , 69.89082075, 70.9150767 ] + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 512 + max_dimension: 512 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.1 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + min_box_overlap_iou: 0.7 + max_box_predictions: 100 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + + keypoint_label_map_path: "PATH_TO_BE_CONFIGURED" + keypoint_estimation_task { + task_name: "human_pose" + task_loss_weight: 1.0 + loss { + localization_loss { + l1_localization_loss { + } + } + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + keypoint_class_name: "/m/01g317" + keypoint_label_to_std { + key: "left_ankle" + value: 0.89 + } + keypoint_label_to_std { + key: "left_ear" + value: 0.35 + } + keypoint_label_to_std { + key: "left_elbow" + value: 0.72 + } + keypoint_label_to_std { + key: "left_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "left_hip" + value: 1.07 + } + keypoint_label_to_std { + key: "left_knee" + value: 0.89 + } + keypoint_label_to_std { + key: "left_shoulder" + value: 0.79 + } + keypoint_label_to_std { + key: "left_wrist" + value: 0.62 + } + keypoint_label_to_std { + key: "nose" + value: 0.26 + } + keypoint_label_to_std { + key: "right_ankle" + value: 0.89 + } + keypoint_label_to_std { + key: "right_ear" + value: 0.35 + } + keypoint_label_to_std { + key: "right_elbow" + value: 0.72 + } + keypoint_label_to_std { + key: "right_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "right_hip" + value: 1.07 + } + keypoint_label_to_std { + key: "right_knee" + value: 0.89 + } + keypoint_label_to_std { + key: "right_shoulder" + value: 0.79 + } + keypoint_label_to_std { + key: "right_wrist" + value: 0.62 + } + keypoint_regression_loss_weight: 0.1 + keypoint_heatmap_loss_weight: 1.0 + keypoint_offset_loss_weight: 1.0 + offset_peak_radius: 3 + per_keypoint_offset: true + } + } +} + +train_config: { + + batch_size: 128 + num_steps: 250000 + + data_augmentation_options { + random_horizontal_flip { + keypoint_flip_permutation: 0 + keypoint_flip_permutation: 2 + keypoint_flip_permutation: 1 + keypoint_flip_permutation: 4 + keypoint_flip_permutation: 3 + keypoint_flip_permutation: 6 + keypoint_flip_permutation: 5 + keypoint_flip_permutation: 8 + keypoint_flip_permutation: 7 + keypoint_flip_permutation: 10 + keypoint_flip_permutation: 9 + keypoint_flip_permutation: 12 + keypoint_flip_permutation: 11 + keypoint_flip_permutation: 14 + keypoint_flip_permutation: 13 + keypoint_flip_permutation: 16 + keypoint_flip_permutation: 15 + } + } + + data_augmentation_options { + random_crop_image { + min_aspect_ratio: 0.5 + max_aspect_ratio: 1.7 + random_coef: 0.25 + } + } + + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_adjust_brightness { + } + } + + data_augmentation_options { + random_absolute_pad_image { + max_height_padding: 200 + max_width_padding: 200 + pad_color: [0, 0, 0] + } + } + + optimizer { + adam_optimizer: { + epsilon: 1e-7 # Match tf.keras.optimizers.Adam's default. + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 1e-3 + total_steps: 250000 + warmup_learning_rate: 2.5e-4 + warmup_steps: 5000 + } + } + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED" + fine_tune_checkpoint_type: "detection" +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + num_keypoints: 17 +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + num_visualizations: 10 + max_num_boxes_to_visualize: 20 + min_score_threshold: 0.2 + batch_size: 1; + parameterized_metric { + coco_keypoint_metrics { + class_label: "person" + keypoint_label_to_sigmas { + key: "nose" + value: 0.026 + } + keypoint_label_to_sigmas { + key: "left_eye" + value: 0.025 + } + keypoint_label_to_sigmas { + key: "right_eye" + value: 0.025 + } + keypoint_label_to_sigmas { + key: "left_ear" + value: 0.035 + } + keypoint_label_to_sigmas { + key: "right_ear" + value: 0.035 + } + keypoint_label_to_sigmas { + key: "left_shoulder" + value: 0.079 + } + keypoint_label_to_sigmas { + key: "right_shoulder" + value: 0.079 + } + keypoint_label_to_sigmas { + key: "left_elbow" + value: 0.072 + } + keypoint_label_to_sigmas { + key: "right_elbow" + value: 0.072 + } + keypoint_label_to_sigmas { + key: "left_wrist" + value: 0.062 + } + keypoint_label_to_sigmas { + key: "right_wrist" + value: 0.062 + } + keypoint_label_to_sigmas { + key: "left_hip" + value: 0.107 + } + keypoint_label_to_sigmas { + key: "right_hip" + value: 0.107 + } + keypoint_label_to_sigmas { + key: "left_knee" + value: 0.087 + } + keypoint_label_to_sigmas { + key: "right_knee" + value: 0.087 + } + keypoint_label_to_sigmas { + key: "left_ankle" + value: 0.089 + } + keypoint_label_to_sigmas { + key: "right_ankle" + value: 0.089 + } + } + } + # Provide the edges to connect the keypoints. The setting is suitable for + # COCO's 17 human pose keypoints. + keypoint_edge { # nose-left eye + start: 0 + end: 1 + } + keypoint_edge { # nose-right eye + start: 0 + end: 2 + } + keypoint_edge { # left eye-left ear + start: 1 + end: 3 + } + keypoint_edge { # right eye-right ear + start: 2 + end: 4 + } + keypoint_edge { # nose-left shoulder + start: 0 + end: 5 + } + keypoint_edge { # nose-right shoulder + start: 0 + end: 6 + } + keypoint_edge { # left shoulder-left elbow + start: 5 + end: 7 + } + keypoint_edge { # left elbow-left wrist + start: 7 + end: 9 + } + keypoint_edge { # right shoulder-right elbow + start: 6 + end: 8 + } + keypoint_edge { # right elbow-right wrist + start: 8 + end: 10 + } + keypoint_edge { # left shoulder-right shoulder + start: 5 + end: 6 + } + keypoint_edge { # left shoulder-left hip + start: 5 + end: 11 + } + keypoint_edge { # right shoulder-right hip + start: 6 + end: 12 + } + keypoint_edge { # left hip-right hip + start: 11 + end: 12 + } + keypoint_edge { # left hip-left knee + start: 11 + end: 13 + } + keypoint_edge { # left knee-left ankle + start: 13 + end: 15 + } + keypoint_edge { # right hip-right knee + start: 12 + end: 14 + } + keypoint_edge { # right knee-right ankle + start: 14 + end: 16 + } +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + num_keypoints: 17 +} + diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet101_v1_fpn_512x512_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet101_v1_fpn_512x512_coco17_tpu-8.config new file mode 100644 index 0000000..2bb7f07 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet101_v1_fpn_512x512_coco17_tpu-8.config @@ -0,0 +1,141 @@ +# CenterNet meta-architecture from the "Objects as Points" [1] paper +# with the ResNet-v1-101 FPN backbone. +# [1]: https://arxiv.org/abs/1904.07850 + +# Train on TPU-8 +# +# Achieves 34.18 mAP on COCO17 Val + + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "resnet_v2_101" + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 512 + max_dimension: 512 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.1 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + min_box_overlap_iou: 0.7 + max_box_predictions: 100 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + } +} + +train_config: { + + batch_size: 128 + num_steps: 140000 + + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_crop_image { + min_aspect_ratio: 0.5 + max_aspect_ratio: 1.7 + random_coef: 0.25 + } + } + + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_adjust_brightness { + } + } + + data_augmentation_options { + random_absolute_pad_image { + max_height_padding: 200 + max_width_padding: 200 + pad_color: [0, 0, 0] + } + } + + optimizer { + adam_optimizer: { + epsilon: 1e-7 # Match tf.keras.optimizers.Adam's default. + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 1e-3 + schedule { + step: 90000 + learning_rate: 1e-4 + } + schedule { + step: 120000 + learning_rate: 1e-5 + } + } + } + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/weights-1" + fine_tune_checkpoint_type: "classification" +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} + diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v1_fpn_512x512_kpts_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v1_fpn_512x512_kpts_coco17_tpu-8.config new file mode 100644 index 0000000..ad25d5c --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v1_fpn_512x512_kpts_coco17_tpu-8.config @@ -0,0 +1,392 @@ +# CenterNet meta-architecture from the "Objects as Points" [1] paper +# with the ResNet-v1-50 backbone. The ResNet backbone has a few differences +# as compared to the one mentioned in the paper, hence the performance is +# slightly worse. This config is TPU comptatible. +# [1]: https://arxiv.org/abs/1904.07850 +# + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "resnet_v1_50_fpn" + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 512 + max_dimension: 512 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.1 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + min_box_overlap_iou: 0.7 + max_box_predictions: 100 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + keypoint_label_map_path: "PATH_TO_BE_CONFIGURED" + keypoint_estimation_task { + task_name: "human_pose" + task_loss_weight: 1.0 + loss { + localization_loss { + l1_localization_loss { + } + } + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + keypoint_class_name: "/m/01g317" + keypoint_label_to_std { + key: "left_ankle" + value: 0.89 + } + keypoint_label_to_std { + key: "left_ear" + value: 0.35 + } + keypoint_label_to_std { + key: "left_elbow" + value: 0.72 + } + keypoint_label_to_std { + key: "left_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "left_hip" + value: 1.07 + } + keypoint_label_to_std { + key: "left_knee" + value: 0.89 + } + keypoint_label_to_std { + key: "left_shoulder" + value: 0.79 + } + keypoint_label_to_std { + key: "left_wrist" + value: 0.62 + } + keypoint_label_to_std { + key: "nose" + value: 0.26 + } + keypoint_label_to_std { + key: "right_ankle" + value: 0.89 + } + keypoint_label_to_std { + key: "right_ear" + value: 0.35 + } + keypoint_label_to_std { + key: "right_elbow" + value: 0.72 + } + keypoint_label_to_std { + key: "right_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "right_hip" + value: 1.07 + } + keypoint_label_to_std { + key: "right_knee" + value: 0.89 + } + keypoint_label_to_std { + key: "right_shoulder" + value: 0.79 + } + keypoint_label_to_std { + key: "right_wrist" + value: 0.62 + } + keypoint_regression_loss_weight: 0.1 + keypoint_heatmap_loss_weight: 1.0 + keypoint_offset_loss_weight: 1.0 + offset_peak_radius: 3 + per_keypoint_offset: true + } + } +} + +train_config: { + + batch_size: 128 + num_steps: 250000 + + data_augmentation_options { + random_horizontal_flip { + keypoint_flip_permutation: 0 + keypoint_flip_permutation: 2 + keypoint_flip_permutation: 1 + keypoint_flip_permutation: 4 + keypoint_flip_permutation: 3 + keypoint_flip_permutation: 6 + keypoint_flip_permutation: 5 + keypoint_flip_permutation: 8 + keypoint_flip_permutation: 7 + keypoint_flip_permutation: 10 + keypoint_flip_permutation: 9 + keypoint_flip_permutation: 12 + keypoint_flip_permutation: 11 + keypoint_flip_permutation: 14 + keypoint_flip_permutation: 13 + keypoint_flip_permutation: 16 + keypoint_flip_permutation: 15 + } + } + + data_augmentation_options { + random_crop_image { + min_aspect_ratio: 0.5 + max_aspect_ratio: 1.7 + random_coef: 0.25 + } + } + + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_adjust_brightness { + } + } + + data_augmentation_options { + random_absolute_pad_image { + max_height_padding: 200 + max_width_padding: 200 + pad_color: [0, 0, 0] + } + } + + optimizer { + adam_optimizer: { + epsilon: 1e-7 # Match tf.keras.optimizers.Adam's default. + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 1e-3 + total_steps: 250000 + warmup_learning_rate: 2.5e-4 + warmup_steps: 5000 + } + } + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED" + fine_tune_checkpoint_type: "classification" +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + num_keypoints: 17 +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + num_visualizations: 10 + max_num_boxes_to_visualize: 20 + min_score_threshold: 0.2 + batch_size: 1; + parameterized_metric { + coco_keypoint_metrics { + class_label: "person" + keypoint_label_to_sigmas { + key: "nose" + value: 0.026 + } + keypoint_label_to_sigmas { + key: "left_eye" + value: 0.025 + } + keypoint_label_to_sigmas { + key: "right_eye" + value: 0.025 + } + keypoint_label_to_sigmas { + key: "left_ear" + value: 0.035 + } + keypoint_label_to_sigmas { + key: "right_ear" + value: 0.035 + } + keypoint_label_to_sigmas { + key: "left_shoulder" + value: 0.079 + } + keypoint_label_to_sigmas { + key: "right_shoulder" + value: 0.079 + } + keypoint_label_to_sigmas { + key: "left_elbow" + value: 0.072 + } + keypoint_label_to_sigmas { + key: "right_elbow" + value: 0.072 + } + keypoint_label_to_sigmas { + key: "left_wrist" + value: 0.062 + } + keypoint_label_to_sigmas { + key: "right_wrist" + value: 0.062 + } + keypoint_label_to_sigmas { + key: "left_hip" + value: 0.107 + } + keypoint_label_to_sigmas { + key: "right_hip" + value: 0.107 + } + keypoint_label_to_sigmas { + key: "left_knee" + value: 0.087 + } + keypoint_label_to_sigmas { + key: "right_knee" + value: 0.087 + } + keypoint_label_to_sigmas { + key: "left_ankle" + value: 0.089 + } + keypoint_label_to_sigmas { + key: "right_ankle" + value: 0.089 + } + } + } + # Provide the edges to connect the keypoints. The setting is suitable for + # COCO's 17 human pose keypoints. + keypoint_edge { # nose-left eye + start: 0 + end: 1 + } + keypoint_edge { # nose-right eye + start: 0 + end: 2 + } + keypoint_edge { # left eye-left ear + start: 1 + end: 3 + } + keypoint_edge { # right eye-right ear + start: 2 + end: 4 + } + keypoint_edge { # nose-left shoulder + start: 0 + end: 5 + } + keypoint_edge { # nose-right shoulder + start: 0 + end: 6 + } + keypoint_edge { # left shoulder-left elbow + start: 5 + end: 7 + } + keypoint_edge { # left elbow-left wrist + start: 7 + end: 9 + } + keypoint_edge { # right shoulder-right elbow + start: 6 + end: 8 + } + keypoint_edge { # right elbow-right wrist + start: 8 + end: 10 + } + keypoint_edge { # left shoulder-right shoulder + start: 5 + end: 6 + } + keypoint_edge { # left shoulder-left hip + start: 5 + end: 11 + } + keypoint_edge { # right shoulder-right hip + start: 6 + end: 12 + } + keypoint_edge { # left hip-right hip + start: 11 + end: 12 + } + keypoint_edge { # left hip-left knee + start: 11 + end: 13 + } + keypoint_edge { # left knee-left ankle + start: 13 + end: 15 + } + keypoint_edge { # right hip-right knee + start: 12 + end: 14 + } + keypoint_edge { # right knee-right ankle + start: 14 + end: 16 + } +} +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } + num_keypoints: 17 +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v2_512x512_kpts_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v2_512x512_kpts_coco17_tpu-8.config new file mode 100644 index 0000000..3067ed4 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/centernet_resnet50_v2_512x512_kpts_coco17_tpu-8.config @@ -0,0 +1,393 @@ +# CenterNet meta-architecture from the "Objects as Points" [1] paper +# with the ResNet-v2-50 backbone. The ResNet backbone has a few differences +# as compared to the one mentioned in the paper, hence the performance is +# slightly worse. This config is TPU comptatible. +# [1]: https://arxiv.org/abs/1904.07850 + +model { + center_net { + num_classes: 90 + feature_extractor { + type: "resnet_v2_50" + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 512 + max_dimension: 512 + pad_to_max_dimension: true + } + } + object_detection_task { + task_loss_weight: 1.0 + offset_loss_weight: 1.0 + scale_loss_weight: 0.1 + localization_loss { + l1_localization_loss { + } + } + } + object_center_params { + object_center_loss_weight: 1.0 + min_box_overlap_iou: 0.7 + max_box_predictions: 100 + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + + keypoint_label_map_path: "PATH_TO_BE_CONFIGURED" + keypoint_estimation_task { + task_name: "human_pose" + task_loss_weight: 1.0 + loss { + localization_loss { + l1_localization_loss { + } + } + classification_loss { + penalty_reduced_logistic_focal_loss { + alpha: 2.0 + beta: 4.0 + } + } + } + keypoint_class_name: "/m/01g317" + keypoint_label_to_std { + key: "left_ankle" + value: 0.89 + } + keypoint_label_to_std { + key: "left_ear" + value: 0.35 + } + keypoint_label_to_std { + key: "left_elbow" + value: 0.72 + } + keypoint_label_to_std { + key: "left_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "left_hip" + value: 1.07 + } + keypoint_label_to_std { + key: "left_knee" + value: 0.89 + } + keypoint_label_to_std { + key: "left_shoulder" + value: 0.79 + } + keypoint_label_to_std { + key: "left_wrist" + value: 0.62 + } + keypoint_label_to_std { + key: "nose" + value: 0.26 + } + keypoint_label_to_std { + key: "right_ankle" + value: 0.89 + } + keypoint_label_to_std { + key: "right_ear" + value: 0.35 + } + keypoint_label_to_std { + key: "right_elbow" + value: 0.72 + } + keypoint_label_to_std { + key: "right_eye" + value: 0.25 + } + keypoint_label_to_std { + key: "right_hip" + value: 1.07 + } + keypoint_label_to_std { + key: "right_knee" + value: 0.89 + } + keypoint_label_to_std { + key: "right_shoulder" + value: 0.79 + } + keypoint_label_to_std { + key: "right_wrist" + value: 0.62 + } + keypoint_regression_loss_weight: 0.1 + keypoint_heatmap_loss_weight: 1.0 + keypoint_offset_loss_weight: 1.0 + offset_peak_radius: 3 + per_keypoint_offset: true + } + } +} + +train_config: { + + batch_size: 128 + num_steps: 250000 + + data_augmentation_options { + random_horizontal_flip { + keypoint_flip_permutation: 0 + keypoint_flip_permutation: 2 + keypoint_flip_permutation: 1 + keypoint_flip_permutation: 4 + keypoint_flip_permutation: 3 + keypoint_flip_permutation: 6 + keypoint_flip_permutation: 5 + keypoint_flip_permutation: 8 + keypoint_flip_permutation: 7 + keypoint_flip_permutation: 10 + keypoint_flip_permutation: 9 + keypoint_flip_permutation: 12 + keypoint_flip_permutation: 11 + keypoint_flip_permutation: 14 + keypoint_flip_permutation: 13 + keypoint_flip_permutation: 16 + keypoint_flip_permutation: 15 + } + } + + data_augmentation_options { + random_crop_image { + min_aspect_ratio: 0.5 + max_aspect_ratio: 1.7 + random_coef: 0.25 + } + } + + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_adjust_brightness { + } + } + + data_augmentation_options { + random_absolute_pad_image { + max_height_padding: 200 + max_width_padding: 200 + pad_color: [0, 0, 0] + } + } + + optimizer { + adam_optimizer: { + epsilon: 1e-7 # Match tf.keras.optimizers.Adam's default. + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 1e-3 + total_steps: 250000 + warmup_learning_rate: 2.5e-4 + warmup_steps: 5000 + } + } + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED" + fine_tune_checkpoint_type: "classification" +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + num_keypoints: 17 +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + num_visualizations: 10 + max_num_boxes_to_visualize: 20 + min_score_threshold: 0.2 + batch_size: 1; + parameterized_metric { + coco_keypoint_metrics { + class_label: "person" + keypoint_label_to_sigmas { + key: "nose" + value: 0.026 + } + keypoint_label_to_sigmas { + key: "left_eye" + value: 0.025 + } + keypoint_label_to_sigmas { + key: "right_eye" + value: 0.025 + } + keypoint_label_to_sigmas { + key: "left_ear" + value: 0.035 + } + keypoint_label_to_sigmas { + key: "right_ear" + value: 0.035 + } + keypoint_label_to_sigmas { + key: "left_shoulder" + value: 0.079 + } + keypoint_label_to_sigmas { + key: "right_shoulder" + value: 0.079 + } + keypoint_label_to_sigmas { + key: "left_elbow" + value: 0.072 + } + keypoint_label_to_sigmas { + key: "right_elbow" + value: 0.072 + } + keypoint_label_to_sigmas { + key: "left_wrist" + value: 0.062 + } + keypoint_label_to_sigmas { + key: "right_wrist" + value: 0.062 + } + keypoint_label_to_sigmas { + key: "left_hip" + value: 0.107 + } + keypoint_label_to_sigmas { + key: "right_hip" + value: 0.107 + } + keypoint_label_to_sigmas { + key: "left_knee" + value: 0.087 + } + keypoint_label_to_sigmas { + key: "right_knee" + value: 0.087 + } + keypoint_label_to_sigmas { + key: "left_ankle" + value: 0.089 + } + keypoint_label_to_sigmas { + key: "right_ankle" + value: 0.089 + } + } + } + # Provide the edges to connect the keypoints. The setting is suitable for + # COCO's 17 human pose keypoints. + keypoint_edge { # nose-left eye + start: 0 + end: 1 + } + keypoint_edge { # nose-right eye + start: 0 + end: 2 + } + keypoint_edge { # left eye-left ear + start: 1 + end: 3 + } + keypoint_edge { # right eye-right ear + start: 2 + end: 4 + } + keypoint_edge { # nose-left shoulder + start: 0 + end: 5 + } + keypoint_edge { # nose-right shoulder + start: 0 + end: 6 + } + keypoint_edge { # left shoulder-left elbow + start: 5 + end: 7 + } + keypoint_edge { # left elbow-left wrist + start: 7 + end: 9 + } + keypoint_edge { # right shoulder-right elbow + start: 6 + end: 8 + } + keypoint_edge { # right elbow-right wrist + start: 8 + end: 10 + } + keypoint_edge { # left shoulder-right shoulder + start: 5 + end: 6 + } + keypoint_edge { # left shoulder-left hip + start: 5 + end: 11 + } + keypoint_edge { # right shoulder-right hip + start: 6 + end: 12 + } + keypoint_edge { # left hip-right hip + start: 11 + end: 12 + } + keypoint_edge { # left hip-left knee + start: 11 + end: 13 + } + keypoint_edge { # left knee-left ankle + start: 13 + end: 15 + } + keypoint_edge { # right hip-right knee + start: 12 + end: 14 + } + keypoint_edge { # right knee-right ankle + start: 14 + end: 16 + } +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } + num_keypoints: 17 +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_1024x1024_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_1024x1024_coco17_tpu-8.config new file mode 100644 index 0000000..c38f6b9 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_1024x1024_coco17_tpu-8.config @@ -0,0 +1,166 @@ +# Faster R-CNN with Resnet-101 (v1), +# w/high res inputs, long training schedule +# Trained on COCO, initialized from Imagenet classification checkpoint +# +# Train on TPU-8 +# +# Achieves 37.1 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + fixed_shape_resizer { + width: 1024 + height: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet101_keras' + batch_norm_trainable: true + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + share_box_across_classes: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 100000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 100000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet101.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true # works only on TPUs +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..af07c7d --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_640x640_coco17_tpu-8.config @@ -0,0 +1,145 @@ +# Faster R-CNN with Resnet-50 (v1) +# Trained on COCO, initialized from Imagenet classification checkpoint +# +# Train on TPU-8 +# +# Achieves 31.8 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 640 + max_dimension: 640 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet101_keras' + batch_norm_trainable: true + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + share_box_across_classes: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 25000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet101.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true # works only on TPUs +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_800x1333_coco17_gpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_800x1333_coco17_gpu-8.config new file mode 100644 index 0000000..8eb4da0 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet101_v1_800x1333_coco17_gpu-8.config @@ -0,0 +1,154 @@ +# Faster R-CNN with Resnet-101 (v1), +# Initialized from Imagenet classification checkpoint +# +# Train on GPU-8 +# +# Achieves 36.6 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 800 + max_dimension: 1333 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet101_keras' + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 16 + num_steps: 200000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 0.01 + total_steps: 200000 + warmup_learning_rate: 0.0 + warmup_steps: 5000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet101.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_1024x1024_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_1024x1024_coco17_tpu-8.config new file mode 100644 index 0000000..034667f --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_1024x1024_coco17_tpu-8.config @@ -0,0 +1,166 @@ +# Faster R-CNN with Resnet-152 (v1) +# w/high res inputs, long training schedule +# Trained on COCO, initialized from Imagenet classification checkpoint +# +# Train on TPU-8 +# +# Achieves 37.6 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + fixed_shape_resizer { + width: 1024 + height: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet152_keras' + batch_norm_trainable: true + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + share_box_across_classes: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 100000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 100000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet152.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true # works only on TPUs +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..525c4ac --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_640x640_coco17_tpu-8.config @@ -0,0 +1,145 @@ +# Faster R-CNN with Resnet-152 (v1) +# Trained on COCO, initialized from Imagenet classification checkpoint +# +# Train on TPU-8 +# +# Achieves 32.4 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 640 + max_dimension: 640 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet152_keras' + batch_norm_trainable: true + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + share_box_across_classes: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 25000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet152.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true # works only on TPUs +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_800x1333_coco17_gpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_800x1333_coco17_gpu-8.config new file mode 100644 index 0000000..8d1879f --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet152_v1_800x1333_coco17_gpu-8.config @@ -0,0 +1,154 @@ +# Faster R-CNN with Resnet-152 (v1), +# Initialized from Imagenet classification checkpoint +# +# Train on GPU-8 +# +# Achieves 37.3 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 800 + max_dimension: 1333 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet152_keras' + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 16 + num_steps: 200000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 0.01 + total_steps: 200000 + warmup_learning_rate: 0.0 + warmup_steps: 5000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet152.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_1024x1024_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_1024x1024_coco17_tpu-8.config new file mode 100644 index 0000000..b6e590e --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_1024x1024_coco17_tpu-8.config @@ -0,0 +1,166 @@ +# Faster R-CNN with Resnet-50 (v1), +# w/high res inputs, long training schedule +# Trained on COCO, initialized from Imagenet classification checkpoint +# +# Train on TPU-8 +# +# Achieves 31.0 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + fixed_shape_resizer { + width: 1024 + height: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_resnet50_keras' + batch_norm_trainable: true + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + share_box_across_classes: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 100000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 100000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet50.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true # works only on TPUs +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..c8601c6 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_640x640_coco17_tpu-8.config @@ -0,0 +1,145 @@ +# Faster R-CNN with Resnet-50 (v1) with 640x640 input resolution +# Trained on COCO, initialized from Imagenet classification checkpoint +# +# Train on TPU-8 +# +# Achieves 29.3 mAP on COCO17 Val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 640 + max_dimension: 640 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet50_keras' + batch_norm_trainable: true + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + share_box_across_classes: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 25000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet50.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true # works only on TPUs +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_800x1333_coco17_gpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_800x1333_coco17_gpu-8.config new file mode 100644 index 0000000..264be5f --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_800x1333_coco17_gpu-8.config @@ -0,0 +1,154 @@ +# Faster R-CNN with Resnet-50 (v1), +# Initialized from Imagenet classification checkpoint +# +# Train on GPU-8 +# +# Achieves 31.4 mAP on COCO17 val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 800 + max_dimension: 1333 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet50_keras' + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + } +} + +train_config: { + batch_size: 16 + num_steps: 200000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 0.01 + total_steps: 200000 + warmup_learning_rate: 0.0 + warmup_steps: 5000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet50.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + data_augmentation_options { + random_adjust_hue { + } + } + + data_augmentation_options { + random_adjust_contrast { + } + } + + data_augmentation_options { + random_adjust_saturation { + } + } + + data_augmentation_options { + random_square_crop_by_scale { + scale_min: 0.6 + scale_max: 1.3 + } + } +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_fpn_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_fpn_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..acb5a91 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/faster_rcnn_resnet50_v1_fpn_640x640_coco17_tpu-8.config @@ -0,0 +1,173 @@ +# Faster RCNN with Resnet 50 v1 FPN feature extractor. +# See Lin et al, https://arxiv.org/abs/1612.03144 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 31.4 mAP on COCO17 Val + +model { + faster_rcnn { + num_classes: 90 + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 640 + max_dimension: 640 + pad_to_max_dimension: true + } + } + feature_extractor { + type: 'faster_rcnn_resnet50_fpn_keras' + batch_norm_trainable: true + fpn { + min_level: 2 + max_level: 6 + } + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + first_stage_anchor_generator { + multiscale_anchor_generator { + min_level: 2 + max_level: 6 + # According to the origial paper the value should be 8.0 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + # According to the original paper the value should be 1 + scales_per_octave: 2 + normalize_coordinates: false + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + # According to the origial paper, value should be 7. + initial_crop_size: 14 + maxpool_kernel_size: 2 + maxpool_stride: 2 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 300 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + use_static_shapes: true + use_matmul_crop_and_resize: true + clip_anchors_to_image: true + use_static_balanced_label_sampler: true + use_matmul_gather_in_matcher: true + } +} + +train_config: { + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 25000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 0.04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet50.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } + + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false + use_bfloat16: true +} + +train_input_reader: { + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/mask_rcnn_inception_resnet_v2_1024x1024_coco17_gpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/mask_rcnn_inception_resnet_v2_1024x1024_coco17_gpu-8.config new file mode 100644 index 0000000..974c1d1 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/mask_rcnn_inception_resnet_v2_1024x1024_coco17_gpu-8.config @@ -0,0 +1,160 @@ +# Mask R-CNN with Inception Resnet v2 (no atrous) +# Sync-trained on COCO (with 8 GPUs) with batch size 16 (1024x1024 resolution) +# Initialized from Imagenet classification checkpoint +# +# Train on GPU-8 +# +# Achieves 40.4 box mAP and 35.5 mask mAP on COCO17 val + +model { + faster_rcnn { + number_of_stages: 3 + num_classes: 90 + image_resizer { + fixed_shape_resizer { + height: 1024 + width: 1024 + } + } + feature_extractor { + type: 'faster_rcnn_inception_resnet_v2_keras' + } + first_stage_anchor_generator { + grid_anchor_generator { + scales: [0.25, 0.5, 1.0, 2.0] + aspect_ratios: [0.5, 1.0, 2.0] + height_stride: 16 + width_stride: 16 + } + } + first_stage_box_predictor_conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + first_stage_nms_score_threshold: 0.0 + first_stage_nms_iou_threshold: 0.7 + first_stage_max_proposals: 300 + first_stage_localization_loss_weight: 2.0 + first_stage_objectness_loss_weight: 1.0 + initial_crop_size: 17 + maxpool_kernel_size: 1 + maxpool_stride: 1 + second_stage_box_predictor { + mask_rcnn_box_predictor { + use_dropout: false + dropout_keep_probability: 1.0 + fc_hyperparams { + op: FC + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + variance_scaling_initializer { + factor: 1.0 + uniform: true + mode: FAN_AVG + } + } + } + mask_height: 33 + mask_width: 33 + mask_prediction_conv_depth: 0 + mask_prediction_num_conv_layers: 4 + conv_hyperparams { + op: CONV + regularizer { + l2_regularizer { + weight: 0.0 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.01 + } + } + } + predict_instance_masks: true + } + } + second_stage_post_processing { + batch_non_max_suppression { + score_threshold: 0.0 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SOFTMAX + } + second_stage_localization_loss_weight: 2.0 + second_stage_classification_loss_weight: 1.0 + second_stage_mask_prediction_loss_weight: 4.0 + resize_masks: false + } +} + +train_config: { + batch_size: 16 + num_steps: 200000 + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 0.008 + total_steps: 200000 + warmup_learning_rate: 0.0 + warmup_steps: 5000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + gradient_clipping_by_norm: 10.0 + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/inception_resnet_v2.ckpt-1" + fine_tune_checkpoint_type: "classification" + data_augmentation_options { + random_horizontal_flip { + } + } +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } + load_instance_masks: true + mask_type: PNG_MASKS +} + +eval_config: { + metrics_set: "coco_detection_metrics" + metrics_set: "coco_mask_metrics" + eval_instance_masks: true + use_moving_averages: false + batch_size: 1 + include_metrics_per_category: true +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } + load_instance_masks: true + mask_type: PNG_MASKS +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d0_512x512_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d0_512x512_coco17_tpu-8.config new file mode 100644 index 0000000..ffcd461 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d0_512x512_coco17_tpu-8.config @@ -0,0 +1,199 @@ + # SSD with EfficientNet-b0 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d0). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b0 checkpoint. +# +# Train on TPU-8 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 512 + max_dimension: 512 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 64 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 3 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b0_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 3 + num_filters: 64 + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 512 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d1_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d1_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..5eacfed --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d1_640x640_coco17_tpu-8.config @@ -0,0 +1,199 @@ + # SSD with EfficientNet-b1 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d1). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b1 checkpoint. +# +# Train on TPU-8 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 640 + max_dimension: 640 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 88 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 3 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b1_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 4 + num_filters: 88 + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 640 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d2_768x768_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d2_768x768_coco17_tpu-8.config new file mode 100644 index 0000000..d2ca75d --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d2_768x768_coco17_tpu-8.config @@ -0,0 +1,199 @@ + # SSD with EfficientNet-b2 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d2). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b2 checkpoint. +# +# Train on TPU-8 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 768 + max_dimension: 768 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 112 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 3 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b2_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 5 + num_filters: 112 + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 768 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d3_896x896_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d3_896x896_coco17_tpu-32.config new file mode 100644 index 0000000..b072d13 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d3_896x896_coco17_tpu-32.config @@ -0,0 +1,199 @@ + # SSD with EfficientNet-b3 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d3). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b3 checkpoint. +# +# Train on TPU-32 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 896 + max_dimension: 896 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 160 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b3_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 6 + num_filters: 160 + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 896 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d4_1024x1024_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d4_1024x1024_coco17_tpu-32.config new file mode 100644 index 0000000..b13b2d4 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d4_1024x1024_coco17_tpu-32.config @@ -0,0 +1,199 @@ + # SSD with EfficientNet-b4 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d4). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b4 checkpoint. +# +# Train on TPU-32 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 1024 + max_dimension: 1024 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 224 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b4_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 7 + num_filters: 224 + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 1024 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d5_1280x1280_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d5_1280x1280_coco17_tpu-32.config new file mode 100644 index 0000000..bcb33d5 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d5_1280x1280_coco17_tpu-32.config @@ -0,0 +1,199 @@ + # SSD with EfficientNet-b5 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d5). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b5 checkpoint. +# +# Train on TPU-32 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 1280 + max_dimension: 1280 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 288 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b5_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 7 + num_filters: 288 + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 1280 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d6_1408x1408_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d6_1408x1408_coco17_tpu-32.config new file mode 100644 index 0000000..1f24607 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d6_1408x1408_coco17_tpu-32.config @@ -0,0 +1,201 @@ + # SSD with EfficientNet-b6 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d6). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b6 checkpoint. +# +# Train on TPU-32 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 1408 + max_dimension: 1408 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 384 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 5 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b6_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 8 + num_filters: 384 + # Use unweighted sum for stability. + combine_method: 'sum' + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 1408 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d7_1536x1536_coco17_tpu-32.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d7_1536x1536_coco17_tpu-32.config new file mode 100644 index 0000000..81954aa --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_efficientdet_d7_1536x1536_coco17_tpu-32.config @@ -0,0 +1,201 @@ + # SSD with EfficientNet-b6 + BiFPN feature extractor, +# shared box predictor and focal loss (a.k.a EfficientDet-d7). +# See EfficientDet, Tan et al, https://arxiv.org/abs/1911.09070 +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from an EfficientNet-b6 checkpoint. +# +# Train on TPU-32 + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + add_background_class: false + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 3 + } + } + image_resizer { + keep_aspect_ratio_resizer { + min_dimension: 1536 + max_dimension: 1536 + pad_to_max_dimension: true + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 384 + class_prediction_bias_init: -4.6 + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true + decay: 0.99 + epsilon: 0.001 + } + } + num_layers_before_predictor: 5 + kernel_size: 3 + use_depthwise: true + } + } + feature_extractor { + type: 'ssd_efficientnet-b6_bifpn_keras' + bifpn { + min_level: 3 + max_level: 7 + num_iterations: 8 + num_filters: 384 + # Use unweighted sum for stability. + combine_method: 'sum' + } + conv_hyperparams { + force_use_bias: true + activation: SWISH + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.99, + epsilon: 0.001, + } + } + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 1.5 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.5 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/ckpt-0" + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 300000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_scale_crop_and_pad_to_square { + output_size: 1536 + scale_min: 0.1 + scale_max: 2.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: 8e-2 + total_steps: 300000 + warmup_learning_rate: .001 + warmup_steps: 2500 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BEE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v1_fpn_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v1_fpn_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..3cfe304 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v1_fpn_640x640_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Mobilenet v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 29.1 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 640 + width: 640 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_mobilenet_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/mobilenet_v1.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 25000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false + batch_size: 1; +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_320x320_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_320x320_coco17_tpu-8.config new file mode 100644 index 0000000..dc3a4a7 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_320x320_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Mobilenet v2 +# Trained on COCO17, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 22.2 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + ssd_anchor_generator { + num_layers: 6 + min_scale: 0.2 + max_scale: 0.95 + aspect_ratios: 1.0 + aspect_ratios: 2.0 + aspect_ratios: 0.5 + aspect_ratios: 3.0 + aspect_ratios: 0.3333 + } + } + image_resizer { + fixed_shape_resizer { + height: 300 + width: 300 + } + } + box_predictor { + convolutional_box_predictor { + min_depth: 0 + max_depth: 0 + num_layers_before_predictor: 0 + use_dropout: false + dropout_keep_probability: 0.8 + kernel_size: 1 + box_code_size: 4 + apply_sigmoid_to_scores: false + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + train: true, + scale: true, + center: true, + decay: 0.97, + epsilon: 0.001, + } + } + } + } + feature_extractor { + type: 'ssd_mobilenet_v2_keras' + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + train: true, + scale: true, + center: true, + decay: 0.97, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.75, + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + delta: 1.0 + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/mobilenet_v2.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 512 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 50000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + ssd_random_crop { + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .8 + total_steps: 50000 + warmup_learning_rate: 0.13333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config new file mode 100644 index 0000000..656e324 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config @@ -0,0 +1,201 @@ +# SSD with Mobilenet v2 FPN-lite (go/fpn-lite) feature extractor, shared box +# predictor and focal loss (a mobile version of Retinanet). +# Retinanet: see Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 22.2 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 320 + width: 320 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 128 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + share_prediction_tower: true + use_depthwise: true + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_mobilenet_v2_fpn_keras' + use_depthwise: true + fpn { + min_level: 3 + max_level: 7 + additional_layer_depth: 128 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/mobilenet_v2.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 50000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .08 + total_steps: 50000 + warmup_learning_rate: .026666 + warmup_steps: 1000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} + diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..5e4bca1 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_640x640_coco17_tpu-8.config @@ -0,0 +1,201 @@ +# SSD with Mobilenet v2 FPN-lite (go/fpn-lite) feature extractor, shared box +# predictor and focal loss (a mobile version of Retinanet). +# Retinanet: see Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 28.2 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 640 + width: 640 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 128 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + share_prediction_tower: true + use_depthwise: true + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_mobilenet_v2_fpn_keras' + use_depthwise: true + fpn { + min_level: 3 + max_level: 7 + additional_layer_depth: 128 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.00004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/mobilenet_v2.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 128 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + num_steps: 50000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .08 + total_steps: 50000 + warmup_learning_rate: .026666 + warmup_steps: 1000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} + diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_1024x1024_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_1024x1024_coco17_tpu-8.config new file mode 100644 index 0000000..015617b --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_1024x1024_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Resnet 101 v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 39.5 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 1024 + width: 1024 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_resnet101_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet101.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 100000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 100000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..37e9b9b --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet101_v1_fpn_640x640_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Resnet 101 v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 35.4 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 640 + width: 640 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_resnet101_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet101.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 25000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_1024x1024_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_1024x1024_coco17_tpu-8.config new file mode 100644 index 0000000..9dbc06e --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_1024x1024_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Resnet 152 v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 39.6 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 1024 + width: 1024 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_resnet152_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet152.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 100000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 100000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..aa99f0a --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet152_v1_fpn_640x640_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Resnet 152 v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 35.6 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 640 + width: 640 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_resnet152_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet152.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 25000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_1024x1024_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_1024x1024_coco17_tpu-8.config new file mode 100644 index 0000000..e1575a0 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_1024x1024_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Resnet 50 v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 38.3 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 1024 + width: 1024 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_resnet50_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet50.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 100000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 100000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.config b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.config new file mode 100644 index 0000000..7164144 --- /dev/null +++ b/workspace/virtuallab/object_detection/configs/tf2/ssd_resnet50_v1_fpn_640x640_coco17_tpu-8.config @@ -0,0 +1,197 @@ +# SSD with Resnet 50 v1 FPN feature extractor, shared box predictor and focal +# loss (a.k.a Retinanet). +# See Lin et al, https://arxiv.org/abs/1708.02002 +# Trained on COCO, initialized from Imagenet classification checkpoint +# Train on TPU-8 +# +# Achieves 34.3 mAP on COCO17 Val + +model { + ssd { + inplace_batchnorm_update: true + freeze_batchnorm: false + num_classes: 90 + box_coder { + faster_rcnn_box_coder { + y_scale: 10.0 + x_scale: 10.0 + height_scale: 5.0 + width_scale: 5.0 + } + } + matcher { + argmax_matcher { + matched_threshold: 0.5 + unmatched_threshold: 0.5 + ignore_thresholds: false + negatives_lower_than_unmatched: true + force_match_for_each_row: true + use_matmul_gather: true + } + } + similarity_calculator { + iou_similarity { + } + } + encode_background_as_zeros: true + anchor_generator { + multiscale_anchor_generator { + min_level: 3 + max_level: 7 + anchor_scale: 4.0 + aspect_ratios: [1.0, 2.0, 0.5] + scales_per_octave: 2 + } + } + image_resizer { + fixed_shape_resizer { + height: 640 + width: 640 + } + } + box_predictor { + weight_shared_convolutional_box_predictor { + depth: 256 + class_prediction_bias_init: -4.6 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + random_normal_initializer { + stddev: 0.01 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + num_layers_before_predictor: 4 + kernel_size: 3 + } + } + feature_extractor { + type: 'ssd_resnet50_v1_fpn_keras' + fpn { + min_level: 3 + max_level: 7 + } + min_depth: 16 + depth_multiplier: 1.0 + conv_hyperparams { + activation: RELU_6, + regularizer { + l2_regularizer { + weight: 0.0004 + } + } + initializer { + truncated_normal_initializer { + stddev: 0.03 + mean: 0.0 + } + } + batch_norm { + scale: true, + decay: 0.997, + epsilon: 0.001, + } + } + override_base_feature_extractor_hyperparams: true + } + loss { + classification_loss { + weighted_sigmoid_focal { + alpha: 0.25 + gamma: 2.0 + } + } + localization_loss { + weighted_smooth_l1 { + } + } + classification_weight: 1.0 + localization_weight: 1.0 + } + normalize_loss_by_num_matches: true + normalize_loc_loss_by_codesize: true + post_processing { + batch_non_max_suppression { + score_threshold: 1e-8 + iou_threshold: 0.6 + max_detections_per_class: 100 + max_total_detections: 100 + } + score_converter: SIGMOID + } + } +} + +train_config: { + fine_tune_checkpoint_version: V2 + fine_tune_checkpoint: "PATH_TO_BE_CONFIGURED/resnet50.ckpt-1" + fine_tune_checkpoint_type: "classification" + batch_size: 64 + sync_replicas: true + startup_delay_steps: 0 + replicas_to_aggregate: 8 + use_bfloat16: true + num_steps: 25000 + data_augmentation_options { + random_horizontal_flip { + } + } + data_augmentation_options { + random_crop_image { + min_object_covered: 0.0 + min_aspect_ratio: 0.75 + max_aspect_ratio: 3.0 + min_area: 0.75 + max_area: 1.0 + overlap_thresh: 0.0 + } + } + optimizer { + momentum_optimizer: { + learning_rate: { + cosine_decay_learning_rate { + learning_rate_base: .04 + total_steps: 25000 + warmup_learning_rate: .013333 + warmup_steps: 2000 + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false + } + max_number_of_boxes: 100 + unpad_groundtruth_tensors: false +} + +train_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/train2017-?????-of-00256.tfrecord" + } +} + +eval_config: { + metrics_set: "coco_detection_metrics" + use_moving_averages: false +} + +eval_input_reader: { + label_map_path: "PATH_TO_BE_CONFIGURED/label_map.txt" + shuffle: false + num_epochs: 1 + tf_record_input_reader { + input_path: "PATH_TO_BE_CONFIGURED/val2017-?????-of-00032.tfrecord" + } +} diff --git a/workspace/virtuallab/object_detection/core/__init__.py b/workspace/virtuallab/object_detection/core/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/__init__.py @@ -0,0 +1 @@ + diff --git a/workspace/virtuallab/object_detection/core/__init__.pyc b/workspace/virtuallab/object_detection/core/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..919631e4c6f9f2d9b906369a0566b5ecba526dff GIT binary patch literal 137 zcmZSn%*(a0Z&rLV0~9a^fU5vQ}wg*lk{`* zQ&Mw^_4AXmQj<&KQ&LNSRAzpjesX?Ms(yTYW?p7Ve7s&kWeEpRn+;HPX-=vg$nX*% GW&i-vT^-B- literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/__init__.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66867758df2f028f36c677546dd97f4871217da9 GIT binary patch literal 142 zcmXr!<>lJgH!Gfzfq~&M5W@jTzyXMhS%5?eLokCTqu)w~B9JhG_+_o1k)NBYpOv4a zpPQeOnp3P_lv^7E8O8^{L{aj`s2SVSnXc1AMb)O_k4ZhAHdEPd(@YaNb=)|^*oBwaB?%e? zXe=mG8tPKHonDgMa_B$kF@L2;Tzkr2$f?h}3qUj_$C=Lb5NQCd7TDc)pZ9&A_gUOn zTWfyvR>!q& zO}A@k*SmGyu7{0o1LGRu%DCBW=6$Q9)@ZG}{=OwvMDwL3nzAuEBWv>Pk#ppB&x!gc zR(q9Sb}j1;SCa9_AI1qA$VetUjgwBZ`EV%NDDJVi&r&p(-(Z1aY0UgY@>H@-3=U&1 z+Ke+l4nyguK^!$Pwiiz$5kvztW>OF7Nj6ndFs|}R6;^xStW4e76g*3?s8Z80`moTL zr~Z(scr39%v=@g{T7idbkOYF!@_|apSo*)IhI}Gbhq2$|iJbBAB$QX!3(3a8V3@KO zJW6TOe80u+lv#zW<0-9U6?{o{O;=qve*n%5*ruA$R

LK8;p#@4N^TkMzY-L0EjH`#C4 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 CX&YMr literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/batcher.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/batcher.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f132b62876cc91b9c24eb6b3895266a3eab27b7 GIT binary patch literal 4769 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 G#(x0*v6-s? literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/box_coder.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/box_coder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89ebf81f264b0e42ccacecfed8e0e8a6c0bbaf60 GIT binary patch literal 5079 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%0AlD}( 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/box_predictor.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/box_predictor.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5603ac54bee5a61f574bba63c5d6c7b6763962da GIT binary patch literal 9991 zcmeHN%X1t@8K2pgR;vecVw3n0=>^dT3W!FY-g$I?{q@)1 z^Edwf!b0PZpZ(VP%Xcj6uU6rx;Q9tm{Q!-yI+n0S$+w54PRXY0vR~e=Y*#xqQR&Qx zYNsx0o!L8BvWZRti#$Ou27(yc~gs*`41N#^=oYpGc@;1P@Xj%1t-y#9dsa##8dZ^-+S z^(2qR=se^j#x?VT(Kuo}5STF(V^^|hFU)55!ay%KuC6vgD3XB&Co*tD;RSux1(Sa0 z^2l@|3XQAZJFR2J6|F;ZcR(Y-|HFaQU^yI)!axR5vVsvUKrrDctkd6TQHVLTX1#IX znu!`-z{0UgC4s5gFci`!fg}WRJN6+1l82Tfu6P83Jrxci7Fsn3m?2Di0j(AfH6O}G zPwtsDO$5)CJMc*05LZfY0J4H{R}oV~9_;7S{|yRZHSKtjcXgz=8x3VN2t_+yz;c~T zYP$2f5r>n`Lb7xAylCKbxx3R>;W!YTr6Jcl#U3uv&WpszFsp|jCtCDfV==o zw8riR&~j{%W_tq&Oe(V>#p%%>O3Jb!M&PZKR4^Vosd}w3+;lNM>}7V)VyqoTw8b!o zLRWT8gqSc0qiZQ0FcrA|SS0E&*$PIi^+UD4%yh`S9!quzYl~o1ybHxk>};3&W2p<& z*vH>pW~KB_S=T7rmbf1%v>kU9B$)9tY$|lt{ubc z5AaNr)>@^5%Tv=$_d9*k8pa)3+ZJ@&Zev2aKYd;`i&sCynN9ZU)MN#zwq3J+bgkkf z0dLZ4#bQ2P=m)K}HXyftSFz9 =ay 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 QRv~?Ji(pV@+Ww8!e+)r0lK=n! literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/keypoint_ops.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/keypoint_ops.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7351571610cf33e085d01040ba3be00c7f27f8be GIT binary patch literal 14050 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/losses.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/losses.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afdadb0fce427785f94fb013affa6c86d9394fa2 GIT binary patch literal 32099 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 Zm&S@X38~M&!cV5|9TWa;zwmh2{|7bSI&1&{ literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/prefetcher.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/prefetcher.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0c15de4663a0000b90487c1956bf6b929c068f8 GIT binary patch literal 2045 zcmbVN&2J+$6t^ccNt&ihSyf!9kS{A5X*bz+IhD467AkQ7mMy1BXylGP$%M%`YkOK! zO;6eDB5~!wog@DRe}k`F;Kq4RyywaE1NO8N+2gU_`}y}i^JHVA_xn%3#DA?3@+Z0V z1hD=ThS`QmkW(V42y!|NrmfRZv_$xloJJxzA%p1h<7Zl(XF?j5mPKMSRTyR!n`@a$ zn@pIMg;Cn?rBVu(6>vIJLgr@pH^7sx6TA+e&T2e`xA_$&ODZy@OR}U_0aAF0-@%f6 zjyo+Na(5@Rt!Y@%s#S$mAVLwnimsw6nno4Mx8bxs?M%C?_gWQI?J->jOG*js^``5h z4buIJronr-vjJcCmbiLHbY2CbThaH)7FhEN1!F+yW^NUiJlBG&F z)(?9<#%=_UzdZhi)k5G5ZWcu{(W+3T$rnr(s}t|ISY(L>@Io*xZK=V#9I%9}h18tc z3Aaq1^Styl5O8&^*B!F&CfQAJ7_3Vp(=vxh08kUBtQg~Zu2mw9fjBjBGpfG9P9ziD zas&X2MFv#)InQ!_mU~+PEP>#cGpUWY_lqC-Yz}8xF)uBfXLFfng=7~KS+IpFnNY=t zmKB&3XB<)mDC`5sqqlfK?FYGd$c)v164%_>Av1Y48_v1rc`ox9X-j?gduKQt4nVzD z@g4&`Arr_@J*?k5cVPc7ed9L;=+{o%xrO#|k1*P}K=se`*kG*h_u+*;FPr7Sh5C3>mi zY5YLM(0lR$)#&0e40!(eWU%Q%G|Gig#{h`!43Gkwy3J=?2&ig|+qc+QU1!xIU1T-S z3fK2H#kwpCsof@s*9@DTb+7ikKJGw=nn8X6X>cajdIJc!FyVMPW|Hfqx#IT8-k{^w zZXlLpTzZ6q+&Jyk0V{TaU90Q|EH4eKD`gWE)C$?6ztW!0KX%*}?7beAl(>r3=n?^}C?q)o$JFgNbo;-2+I%GM7i2 M@MSREA?>&R1@PNfM*si- literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/preprocessor.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/preprocessor.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3b5b73262a41591482c8847b0f3e8ee173370c8 GIT binary patch literal 152375 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:ISg&D6+To1$0ff8KYZH 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 xi}vIoQw_n`e*vx~N4Wq1 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/region_similarity_calculator.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/region_similarity_calculator.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..577a34254eb7d28f9df8f8cf3d8395ad89c3915a GIT binary patch literal 6970 zcmcIo&u<(_74Bd2!()5w#CDPm3)C*K$*hcxcR@%2t>R7CCDO(bNmfFGpwlx|Dj6pI~gV?M_MkhM%tG4x_#cO==k;NB9 zwRc98#n~h0$Zf7+eu*z*ep#$xypD05uVB1l#v2%~@-rBpG2?R>pXF;9ubJ_AjMw=F z#v5jQfmiNWjdSDQ-xm8}JYaXiei#KZ)DPITAZm}IK*v(CPAu8`@%@iNrPWrgcKXBZ zxIYY}U;!Hjp*#$gV0{s&5vJk}Q~3_Hme7Yn44Cc;){5^7#X<}Y<=u7#Zx#-UMn7fk8Z9`F0Kj2CJOD8A zZP`!5{jQMgAc#f=COm8h8n}u9s+qiiCaty?Isc$)$3t<2-5N5}V}qbCw%B#i2}Y4t42r)*HiVyDVqpiLJP?^&w}l?b zK`vUrE_b3h(C@y}U|I|mJej<(8w>^e#oi{n`Ac>f>h6i=$JP?dl1`IKw%uOCO&r}x zY9x_wgiESEnH{n;yu?!?>Lk^vM)KY}Ny$9?7fYT6V*hh%yBqh#b}w#i_hT+1wJn7b zfoylT<5o|!wao>;Ot-uBF`;zM zkYr3_$R%`4e9U5D!yf_7s;7;nf4M$d>g2cs*=K}Eucsd1fZG3Ld`dd@n?lnqs1CPmb)SRb=VDxlI zf>)(zBe$x0o+D{y4v@xWF=-Oo93ZQoSsj~uy!2=Ruq^Wm;92?%S+H3KL>H3!^`G3m zUBKvM>{Nvl`-by67=(!Ja-Q2G(m7z7tv_z2xQ<0 zfegvt@|}r;*3BXr;&VxjkkTmTgo98c&MZVbekx$HhzL8%22-dotSR3>1Ddqp`^Hxh zhGx;`o4D7+WulE`*X*)AK0j}ft?6dD*n~)mPR2ujh0et8S=#P7J@?24cX0|MPkX)6 z#GQEH%Fb`yhb1tZvaa;1DZ5#>Fkav;W@_oo1#8_pZ%s-UtcT@Ed69y+7px$%KjHRI zwIlTY75si*#-jn(a)i8!L^$=toF|-^{^84DHH4Dvm6s9?j(=e>GTenp7r~4NY8Wfw zw;ogSYb+)U`vP2=h;UL)`EauQe#-toF!UI?ma>3QauIJNWiX@uD3T0=G}buLj=@ag z5uzHEW2v)wwxA(D3R+12&DC51)0qvkOYRv&HW1YkkKCb<`10)QV}C_S+N#y}+z`Y$A9AUzL9lokWS6zA7UA zRs1iLcb1ipTO-ZQl4#oF|Kc~z(iQcoPZpbo-&5)4uL(s2C#yDo{RFhUc)s4knwnNO z`5Hi$H1k3*27wF;0RH=P0r*J*xhn;ZGBo^hxGe^mh7LC7gCKQPPz zUKQZ@i$j!X$2d7=q0waMJi)n87hMME&q+adPoxxIEfqa?`gxg22UdC-1r44H*q;;h zGGK#(7Vz`pklw+XzoB(=NYl(5r0EX*o}{1^LqVz9Llo51dI}V@nq0WsMQWl$zuMNYBG!`;W6Ubh}liz^0 z@#b@u2peF83-Km3PdjN+ZlJOhEm)`;L5t?B)pe=>{M@LF%J08R&9qlhbMbONg^64+fs3NFxqqrfKfVfCOvL zgmK^!H2i07IcGCuTCxj>B-6*(}Uw#)m%#Idw+@3t_Pb2`HTdQSo`$gk* zjn_==gKBr35lXM9-AM{$lA@TTl#;kXt1ZbtOyMqZRd_#HPs1c?vxjLRQg0pXBxNJ7 zPMm|CRQVgni=lWJDyQ?atWrZpOX^e{@074iXDTh`6k<4>q^y^Qi3{da>#J+4Z>+AbuKy375RB{q literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/__pycache__/standard_fields.cpython-36.pyc b/workspace/virtuallab/object_detection/core/__pycache__/standard_fields.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71f119f0c1e4265944d978796d1894d9a18c7c91 GIT binary patch literal 14155 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/anchor_generator.py b/workspace/virtuallab/object_detection/core/anchor_generator.py new file mode 100644 index 0000000..69e29d8 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/anchor_generator.py @@ -0,0 +1,171 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Base anchor generator. + +The job of the anchor generator is to create (or load) a collection +of bounding boxes to be used as anchors. + +Generated anchors are assumed to match some convolutional grid or list of grid +shapes. For example, we might want to generate anchors matching an 8x8 +feature map and a 4x4 feature map. If we place 3 anchors per grid location +on the first feature map and 6 anchors per grid location on the second feature +map, then 3*8*8 + 6*4*4 = 288 anchors are generated in total. + +To support fully convolutional settings, feature map shapes are passed +dynamically at generation time. The number of anchors to place at each location +is static --- implementations of AnchorGenerator must always be able return +the number of anchors that it uses per location for each feature map. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from abc import ABCMeta +from abc import abstractmethod + +import six +from six.moves import zip +import tensorflow.compat.v1 as tf + + +class AnchorGenerator(six.with_metaclass(ABCMeta, object)): + """Abstract base class for anchor generators.""" + + @abstractmethod + def name_scope(self): + """Name scope. + + Must be defined by implementations. + + Returns: + a string representing the name scope of the anchor generation operation. + """ + pass + + @property + def check_num_anchors(self): + """Whether to dynamically check the number of anchors generated. + + Can be overridden by implementations that would like to disable this + behavior. + + Returns: + a boolean controlling whether the Generate function should dynamically + check the number of anchors generated against the mathematically + expected number of anchors. + """ + return True + + @abstractmethod + def num_anchors_per_location(self): + """Returns the number of anchors per spatial location. + + Returns: + a list of integers, one for each expected feature map to be passed to + the `generate` function. + """ + pass + + def generate(self, feature_map_shape_list, **params): + """Generates a collection of bounding boxes to be used as anchors. + + TODO(rathodv): remove **params from argument list and make stride and + offsets (for multiple_grid_anchor_generator) constructor arguments. + + Args: + feature_map_shape_list: list of (height, width) pairs in the format + [(height_0, width_0), (height_1, width_1), ...] that the generated + anchors must align with. Pairs can be provided as 1-dimensional + integer tensors of length 2 or simply as tuples of integers. + **params: parameters for anchor generation op + + Returns: + boxes_list: a list of BoxLists each holding anchor boxes corresponding to + the input feature map shapes. + + Raises: + ValueError: if the number of feature map shapes does not match the length + of NumAnchorsPerLocation. + """ + if self.check_num_anchors and ( + len(feature_map_shape_list) != len(self.num_anchors_per_location())): + raise ValueError('Number of feature maps is expected to equal the length ' + 'of `num_anchors_per_location`.') + with tf.name_scope(self.name_scope()): + anchors_list = self._generate(feature_map_shape_list, **params) + if self.check_num_anchors: + with tf.control_dependencies([ + self._assert_correct_number_of_anchors( + anchors_list, feature_map_shape_list)]): + for item in anchors_list: + item.set(tf.identity(item.get())) + return anchors_list + + @abstractmethod + def _generate(self, feature_map_shape_list, **params): + """To be overridden by implementations. + + Args: + feature_map_shape_list: list of (height, width) pairs in the format + [(height_0, width_0), (height_1, width_1), ...] that the generated + anchors must align with. + **params: parameters for anchor generation op + + Returns: + boxes_list: a list of BoxList, each holding a collection of N anchor + boxes. + """ + pass + + def anchor_index_to_feature_map_index(self, boxlist_list): + """Returns a 1-D array of feature map indices for each anchor. + + Args: + boxlist_list: a list of Boxlist, each holding a collection of N anchor + boxes. This list is produced in self.generate(). + + Returns: + A [num_anchors] integer array, where each element indicates which feature + map index the anchor belongs to. + """ + feature_map_indices_list = [] + for i, boxes in enumerate(boxlist_list): + feature_map_indices_list.append( + i * tf.ones([boxes.num_boxes()], dtype=tf.int32)) + return tf.concat(feature_map_indices_list, axis=0) + + def _assert_correct_number_of_anchors(self, anchors_list, + feature_map_shape_list): + """Assert that correct number of anchors was generated. + + Args: + anchors_list: A list of box_list.BoxList object holding anchors generated. + feature_map_shape_list: list of (height, width) pairs in the format + [(height_0, width_0), (height_1, width_1), ...] that the generated + anchors must align with. + Returns: + Op that raises InvalidArgumentError if the number of anchors does not + match the number of expected anchors. + """ + expected_num_anchors = 0 + actual_num_anchors = 0 + for num_anchors_per_location, feature_map_shape, anchors in zip( + self.num_anchors_per_location(), feature_map_shape_list, anchors_list): + expected_num_anchors += (num_anchors_per_location + * feature_map_shape[0] + * feature_map_shape[1]) + actual_num_anchors += anchors.num_boxes() + return tf.assert_equal(expected_num_anchors, actual_num_anchors) diff --git a/workspace/virtuallab/object_detection/core/anchor_generator.pyc b/workspace/virtuallab/object_detection/core/anchor_generator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..927cd2d75218551cab7cec1a17de0566af4b9843 GIT binary patch literal 6684 zcmdT|TXP&o74F%UR?^D0Y{yCP3v@z3t%SUaY?mWckV3Kvq)6njGBGGIb=XB3%bupC!@erw|w=dJ(r_bd(Ur+O2m)l?c z`LBK`+}{j-fAZ85`fsR2LhPaRMAQ(8C(?%4YY30pO>Q^EUemT`xIH8GW^8*_B(oxI ziM^I>x41ng_U3GRP9*bUZ=Tnl7s-N1+hVUh8Cw+lm&E>Mv3Es83qs9{{UxDVLR}S~ zH^cz@x5XcX_*{tJ>|GPloY?IwLR|e;Q;6+AD;Z?rC@;%tb@Sn-s;l9g7G476KR zW4E8#ovE8e!B~k+wXRb1VWTuK;YjK{RoEaq$dih82ogCg<4Dr>u{IGU0m>{rw$!ri2oWWw+Vd| zDtPt~>L?pRH3=#x<~_m~HA|4>n!p8&HtXh`h-Rn`bfNy)x_?1LE$(jKIH$zl zM`TwRG*W{&0|xYuPJ4rUvB$>Nn>uq#ZqI3Lu=Yq3875VbBZ zx6^@W&iEv=R>{D2QNP|qu{HuGtpO?5(mYa$Ud#LYfImM{#-iX_n3rnJYQ=Z42!t(; zOdHb?KFa5FuhEL653z8*!i<2zNR9fpC?AjDEbv~0(sU4pBNaZg$2}8u6CiM!yAPCu zhVuhe0;wWE$yp_1DTK>mUL}CC_?hDUV$ES@MzLl!_0=dih&gSL>CRZBpXZ6fW&ki# zB0Z2r4r}=lq%N8jpvcCG)Fa+;>SWHmU+a7_7zS|${~@y@bBq)Ui&AbGH7;NsxYZea zu!grGNG5+&R*G9a3f+q{{xz(|hw&kTZ{33(%KE^i^L^krXuiUHr|4Iho3Kq6I5@Io zjd8DvnL2v{u+&hM!2LXfIch+hsA~!t$rIo`HW8rVG)pRfT6@~liAB%E(*>*ZTTsW8 zB7Gd*uMhomj9IPDhZJD5crM}j1wqk2P0n0e&+==t?zJzEgh$g_w91Qy_`(w}oAv5{=LvJ%oEI<9kDVG~$oqYHTX^FY z>V5F!ZSitOyujiMJfqF^MYjcZ_nv) z*@;h5Y);r!A&6uaKVWu<)`jt{~hBiP~mAtKm}s z&eYv0Hkhb(J0E`tU2993MtQ}ydr`??nK~IlzzoB4-SUtLoF&j8O zrXip*gWf#-ETE{PLzx?Bxl9U{QK$@=JB*b!Z_#VS%S+?4>On)Smi&BBrx@Op=sm3r z=L0`7N8CA7rbE!k$ZXy~?fmF6Eeu%%Fmx8@?Cc=GM67C5dU1)8l6g}gBKMKBk(XyE7b93HP$$b&f|unUw6#17_;~?`(q$ zJMfL>6V4$)MOBCdg)awm_vzMw*jYZGNp8tcr=af%<}shNM@0t3jYV0XQTAeQ&x>zJ z8iVoi-P^egeRzRJ)R+ObPA)3R@ofT})4Gx&)QZeep%{LGG4A2$5goEKgQtQ}bg@&6 zT|q&h*`q5uPXN``{2GbQF3KxH_B*$E0?-y8ai+kLU~gLehA?RwEu9rDCK49Kyd*Dv zVgAj4Z*}6cl6;ftJv?9~k4?esC(IwvBgGts8pnot#}oh{Mbwwy)UJGwkc zT28D@L{rv*`Awmh95koM+rz?YwPZdeKgbS(B#ywLnY1&dI?Je>B4tgZCIEg+r!27` z^qP<%H_HYkM_^2@Z{voU1reRzcc|dtWAyOVe`F@m#zTcxfVHqCwTXJ+WPn)T>Brt zN%e@^t>WR+;3SQu#ic7t?S=0yU0t}=A(i*KDBLLq6@4Cu 1 + """ + if positive_fraction < 0 or positive_fraction > 1: + raise ValueError('positive_fraction should be in range [0,1]. ' + 'Received: %s.' % positive_fraction) + self._positive_fraction = positive_fraction + self._is_static = is_static + + def _get_num_pos_neg_samples(self, sorted_indices_tensor, sample_size): + """Counts the number of positives and negatives numbers to be sampled. + + Args: + sorted_indices_tensor: A sorted int32 tensor of shape [N] which contains + the signed indices of the examples where the sign is based on the label + value. The examples that cannot be sampled are set to 0. It samples + atmost sample_size*positive_fraction positive examples and remaining + from negative examples. + sample_size: Size of subsamples. + + Returns: + A tuple containing the number of positive and negative labels in the + subsample. + """ + input_length = tf.shape(sorted_indices_tensor)[0] + valid_positive_index = tf.greater(sorted_indices_tensor, + tf.zeros(input_length, tf.int32)) + num_sampled_pos = tf.reduce_sum(tf.cast(valid_positive_index, tf.int32)) + max_num_positive_samples = tf.constant( + int(sample_size * self._positive_fraction), tf.int32) + num_positive_samples = tf.minimum(max_num_positive_samples, num_sampled_pos) + num_negative_samples = tf.constant(sample_size, + tf.int32) - num_positive_samples + + return num_positive_samples, num_negative_samples + + def _get_values_from_start_and_end(self, input_tensor, num_start_samples, + num_end_samples, total_num_samples): + """slices num_start_samples and last num_end_samples from input_tensor. + + Args: + input_tensor: An int32 tensor of shape [N] to be sliced. + num_start_samples: Number of examples to be sliced from the beginning + of the input tensor. + num_end_samples: Number of examples to be sliced from the end of the + input tensor. + total_num_samples: Sum of is num_start_samples and num_end_samples. This + should be a scalar. + + Returns: + A tensor containing the first num_start_samples and last num_end_samples + from input_tensor. + + """ + input_length = tf.shape(input_tensor)[0] + start_positions = tf.less(tf.range(input_length), num_start_samples) + end_positions = tf.greater_equal( + tf.range(input_length), input_length - num_end_samples) + selected_positions = tf.logical_or(start_positions, end_positions) + selected_positions = tf.cast(selected_positions, tf.float32) + indexed_positions = tf.multiply(tf.cumsum(selected_positions), + selected_positions) + one_hot_selector = tf.one_hot(tf.cast(indexed_positions, tf.int32) - 1, + total_num_samples, + dtype=tf.float32) + return tf.cast(tf.tensordot(tf.cast(input_tensor, tf.float32), + one_hot_selector, axes=[0, 0]), tf.int32) + + def _static_subsample(self, indicator, batch_size, labels): + """Returns subsampled minibatch. + + Args: + indicator: boolean tensor of shape [N] whose True entries can be sampled. + N should be a complie time constant. + batch_size: desired batch size. This scalar cannot be None. + labels: boolean tensor of shape [N] denoting positive(=True) and negative + (=False) examples. N should be a complie time constant. + + Returns: + sampled_idx_indicator: boolean tensor of shape [N], True for entries which + are sampled. It ensures the length of output of the subsample is always + batch_size, even when number of examples set to True in indicator is + less than batch_size. + + Raises: + ValueError: if labels and indicator are not 1D boolean tensors. + """ + # Check if indicator and labels have a static size. + if not indicator.shape.is_fully_defined(): + raise ValueError('indicator must be static in shape when is_static is' + 'True') + if not labels.shape.is_fully_defined(): + raise ValueError('labels must be static in shape when is_static is' + 'True') + if not isinstance(batch_size, int): + raise ValueError('batch_size has to be an integer when is_static is' + 'True.') + + input_length = tf.shape(indicator)[0] + + # Set the number of examples set True in indicator to be at least + # batch_size. + num_true_sampled = tf.reduce_sum(tf.cast(indicator, tf.float32)) + additional_false_sample = tf.less_equal( + tf.cumsum(tf.cast(tf.logical_not(indicator), tf.float32)), + batch_size - num_true_sampled) + indicator = tf.logical_or(indicator, additional_false_sample) + + # Shuffle indicator and label. Need to store the permutation to restore the + # order post sampling. + permutation = tf.random_shuffle(tf.range(input_length)) + indicator = tf.gather(indicator, permutation, axis=0) + labels = tf.gather(labels, permutation, axis=0) + + # index (starting from 1) when indicator is True, 0 when False + indicator_idx = tf.where( + indicator, tf.range(1, input_length + 1), + tf.zeros(input_length, tf.int32)) + + # Replace -1 for negative, +1 for positive labels + signed_label = tf.where( + labels, tf.ones(input_length, tf.int32), + tf.scalar_mul(-1, tf.ones(input_length, tf.int32))) + # negative of index for negative label, positive index for positive label, + # 0 when indicator is False. + signed_indicator_idx = tf.multiply(indicator_idx, signed_label) + sorted_signed_indicator_idx = tf.nn.top_k( + signed_indicator_idx, input_length, sorted=True).values + + [num_positive_samples, + num_negative_samples] = self._get_num_pos_neg_samples( + sorted_signed_indicator_idx, batch_size) + + sampled_idx = self._get_values_from_start_and_end( + sorted_signed_indicator_idx, num_positive_samples, + num_negative_samples, batch_size) + + # Shift the indices to start from 0 and remove any samples that are set as + # False. + sampled_idx = tf.abs(sampled_idx) - tf.ones(batch_size, tf.int32) + sampled_idx = tf.multiply( + tf.cast(tf.greater_equal(sampled_idx, tf.constant(0)), tf.int32), + sampled_idx) + + sampled_idx_indicator = tf.cast(tf.reduce_sum( + tf.one_hot(sampled_idx, depth=input_length), + axis=0), tf.bool) + + # project back the order based on stored permutations + idx_indicator = tf.scatter_nd( + tf.expand_dims(permutation, -1), sampled_idx_indicator, + shape=(input_length,)) + return idx_indicator + + def subsample(self, indicator, batch_size, labels, scope=None): + """Returns subsampled minibatch. + + Args: + indicator: boolean tensor of shape [N] whose True entries can be sampled. + batch_size: desired batch size. If None, keeps all positive samples and + randomly selects negative samples so that the positive sample fraction + matches self._positive_fraction. It cannot be None is is_static is True. + labels: boolean tensor of shape [N] denoting positive(=True) and negative + (=False) examples. + scope: name scope. + + Returns: + sampled_idx_indicator: boolean tensor of shape [N], True for entries which + are sampled. + + Raises: + ValueError: if labels and indicator are not 1D boolean tensors. + """ + if len(indicator.get_shape().as_list()) != 1: + raise ValueError('indicator must be 1 dimensional, got a tensor of ' + 'shape %s' % indicator.get_shape()) + if len(labels.get_shape().as_list()) != 1: + raise ValueError('labels must be 1 dimensional, got a tensor of ' + 'shape %s' % labels.get_shape()) + if labels.dtype != tf.bool: + raise ValueError('labels should be of type bool. Received: %s' % + labels.dtype) + if indicator.dtype != tf.bool: + raise ValueError('indicator should be of type bool. Received: %s' % + indicator.dtype) + with tf.name_scope(scope, 'BalancedPositiveNegativeSampler'): + if self._is_static: + return self._static_subsample(indicator, batch_size, labels) + + else: + # Only sample from indicated samples + negative_idx = tf.logical_not(labels) + positive_idx = tf.logical_and(labels, indicator) + negative_idx = tf.logical_and(negative_idx, indicator) + + # Sample positive and negative samples separately + if batch_size is None: + max_num_pos = tf.reduce_sum(tf.cast(positive_idx, dtype=tf.int32)) + else: + max_num_pos = int(self._positive_fraction * batch_size) + sampled_pos_idx = self.subsample_indicator(positive_idx, max_num_pos) + num_sampled_pos = tf.reduce_sum(tf.cast(sampled_pos_idx, tf.int32)) + if batch_size is None: + negative_positive_ratio = ( + 1 - self._positive_fraction) / self._positive_fraction + max_num_neg = tf.cast( + negative_positive_ratio * + tf.cast(num_sampled_pos, dtype=tf.float32), + dtype=tf.int32) + else: + max_num_neg = batch_size - num_sampled_pos + sampled_neg_idx = self.subsample_indicator(negative_idx, max_num_neg) + + return tf.logical_or(sampled_pos_idx, sampled_neg_idx) diff --git a/workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler.pyc b/workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03838230390e344dd6623e107b2a3a627d521150 GIT binary patch literal 9511 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 JORbCT{{oz;JB|PV literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler_test.py b/workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler_test.py new file mode 100644 index 0000000..10b8ca7 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/balanced_positive_negative_sampler_test.py @@ -0,0 +1,212 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.balanced_positive_negative_sampler.""" + +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import balanced_positive_negative_sampler +from object_detection.utils import test_case + + +class BalancedPositiveNegativeSamplerTest(test_case.TestCase): + + def test_subsample_all_examples(self): + if self.has_tpu(): return + numpy_labels = np.random.permutation(300) + indicator = np.array(np.ones(300) == 1, np.bool) + numpy_labels = (numpy_labels - 200) > 0 + + labels = np.array(numpy_labels, np.bool) + + def graph_fn(indicator, labels): + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler()) + return sampler.subsample(indicator, 64, labels) + + is_sampled = self.execute_cpu(graph_fn, [indicator, labels]) + self.assertEqual(sum(is_sampled), 64) + self.assertEqual(sum(np.logical_and(numpy_labels, is_sampled)), 32) + self.assertEqual(sum(np.logical_and( + np.logical_not(numpy_labels), is_sampled)), 32) + + def test_subsample_all_examples_static(self): + if not self.has_tpu(): return + numpy_labels = np.random.permutation(300) + indicator = np.array(np.ones(300) == 1, np.bool) + numpy_labels = (numpy_labels - 200) > 0 + + labels = np.array(numpy_labels, np.bool) + + def graph_fn(indicator, labels): + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler( + is_static=True)) + return sampler.subsample(indicator, 64, labels) + + is_sampled = self.execute_tpu(graph_fn, [indicator, labels]) + self.assertEqual(sum(is_sampled), 64) + self.assertEqual(sum(np.logical_and(numpy_labels, is_sampled)), 32) + self.assertEqual(sum(np.logical_and( + np.logical_not(numpy_labels), is_sampled)), 32) + + def test_subsample_selection(self): + if self.has_tpu(): return + # Test random sampling when only some examples can be sampled: + # 100 samples, 20 positives, 10 positives cannot be sampled. + numpy_labels = np.arange(100) + numpy_indicator = numpy_labels < 90 + indicator = np.array(numpy_indicator, np.bool) + numpy_labels = (numpy_labels - 80) >= 0 + + labels = np.array(numpy_labels, np.bool) + + def graph_fn(indicator, labels): + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler()) + return sampler.subsample(indicator, 64, labels) + + is_sampled = self.execute_cpu(graph_fn, [indicator, labels]) + self.assertEqual(sum(is_sampled), 64) + self.assertEqual(sum(np.logical_and(numpy_labels, is_sampled)), 10) + self.assertEqual(sum(np.logical_and( + np.logical_not(numpy_labels), is_sampled)), 54) + self.assertAllEqual(is_sampled, np.logical_and(is_sampled, numpy_indicator)) + + def test_subsample_selection_static(self): + if not self.has_tpu(): return + # Test random sampling when only some examples can be sampled: + # 100 samples, 20 positives, 10 positives cannot be sampled. + numpy_labels = np.arange(100) + numpy_indicator = numpy_labels < 90 + indicator = np.array(numpy_indicator, np.bool) + numpy_labels = (numpy_labels - 80) >= 0 + + labels = np.array(numpy_labels, np.bool) + + def graph_fn(indicator, labels): + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler( + is_static=True)) + return sampler.subsample(indicator, 64, labels) + + is_sampled = self.execute_tpu(graph_fn, [indicator, labels]) + self.assertEqual(sum(is_sampled), 64) + self.assertEqual(sum(np.logical_and(numpy_labels, is_sampled)), 10) + self.assertEqual(sum(np.logical_and( + np.logical_not(numpy_labels), is_sampled)), 54) + self.assertAllEqual(is_sampled, np.logical_and(is_sampled, numpy_indicator)) + + def test_subsample_selection_larger_batch_size(self): + if self.has_tpu(): return + # Test random sampling when total number of examples that can be sampled are + # less than batch size: + # 100 samples, 50 positives, 40 positives cannot be sampled, batch size 64. + # It should still return 64 samples, with 4 of them that couldn't have been + # sampled. + numpy_labels = np.arange(100) + numpy_indicator = numpy_labels < 60 + indicator = np.array(numpy_indicator, np.bool) + numpy_labels = (numpy_labels - 50) >= 0 + + labels = np.array(numpy_labels, np.bool) + + def graph_fn(indicator, labels): + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler()) + return sampler.subsample(indicator, 64, labels) + + is_sampled = self.execute_cpu(graph_fn, [indicator, labels]) + self.assertEqual(sum(is_sampled), 60) + self.assertGreaterEqual(sum(np.logical_and(numpy_labels, is_sampled)), 10) + self.assertGreaterEqual( + sum(np.logical_and(np.logical_not(numpy_labels), is_sampled)), 50) + self.assertEqual(sum(np.logical_and(is_sampled, numpy_indicator)), 60) + + def test_subsample_selection_larger_batch_size_static(self): + if not self.has_tpu(): return + # Test random sampling when total number of examples that can be sampled are + # less than batch size: + # 100 samples, 50 positives, 40 positives cannot be sampled, batch size 64. + # It should still return 64 samples, with 4 of them that couldn't have been + # sampled. + numpy_labels = np.arange(100) + numpy_indicator = numpy_labels < 60 + indicator = np.array(numpy_indicator, np.bool) + numpy_labels = (numpy_labels - 50) >= 0 + + labels = np.array(numpy_labels, np.bool) + + def graph_fn(indicator, labels): + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler( + is_static=True)) + return sampler.subsample(indicator, 64, labels) + + is_sampled = self.execute_tpu(graph_fn, [indicator, labels]) + self.assertEqual(sum(is_sampled), 64) + self.assertGreaterEqual(sum(np.logical_and(numpy_labels, is_sampled)), 10) + self.assertGreaterEqual( + sum(np.logical_and(np.logical_not(numpy_labels), is_sampled)), 50) + self.assertEqual(sum(np.logical_and(is_sampled, numpy_indicator)), 60) + + def test_subsample_selection_no_batch_size(self): + if self.has_tpu(): return + # Test random sampling when only some examples can be sampled: + # 1000 samples, 6 positives (5 can be sampled). + numpy_labels = np.arange(1000) + numpy_indicator = numpy_labels < 999 + numpy_labels = (numpy_labels - 994) >= 0 + + def graph_fn(indicator, labels): + sampler = (balanced_positive_negative_sampler. + BalancedPositiveNegativeSampler(0.01)) + is_sampled = sampler.subsample(indicator, None, labels) + return is_sampled + is_sampled_out = self.execute_cpu(graph_fn, [numpy_indicator, numpy_labels]) + self.assertEqual(sum(is_sampled_out), 500) + self.assertEqual(sum(np.logical_and(numpy_labels, is_sampled_out)), 5) + self.assertEqual(sum(np.logical_and( + np.logical_not(numpy_labels), is_sampled_out)), 495) + self.assertAllEqual(is_sampled_out, np.logical_and(is_sampled_out, + numpy_indicator)) + + def test_subsample_selection_no_batch_size_static(self): + labels = tf.constant([[True, False, False]]) + indicator = tf.constant([True, False, True]) + sampler = ( + balanced_positive_negative_sampler.BalancedPositiveNegativeSampler()) + with self.assertRaises(ValueError): + sampler.subsample(indicator, None, labels) + + def test_raises_error_with_incorrect_label_shape(self): + labels = tf.constant([[True, False, False]]) + indicator = tf.constant([True, False, True]) + sampler = (balanced_positive_negative_sampler. + BalancedPositiveNegativeSampler()) + with self.assertRaises(ValueError): + sampler.subsample(indicator, 64, labels) + + def test_raises_error_with_incorrect_indicator_shape(self): + labels = tf.constant([True, False, False]) + indicator = tf.constant([[True, False, True]]) + sampler = (balanced_positive_negative_sampler. + BalancedPositiveNegativeSampler()) + with self.assertRaises(ValueError): + sampler.subsample(indicator, 64, labels) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/batch_multiclass_nms_test.py b/workspace/virtuallab/object_detection/core/batch_multiclass_nms_test.py new file mode 100644 index 0000000..06f1710 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/batch_multiclass_nms_test.py @@ -0,0 +1,686 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for google3.third_party.tensorflow_models.object_detection.core.batch_multiclass_nms.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from absl.testing import parameterized +import numpy as np +from six.moves import range +import tensorflow.compat.v1 as tf +from object_detection.core import post_processing +from object_detection.utils import test_case + + +class BatchMulticlassNonMaxSuppressionTest(test_case.TestCase, + parameterized.TestCase): + + def test_batch_multiclass_nms_with_batch_size_1(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]], + [[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0], + [.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = [[[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 999, 2, 1004], + [0, 100, 1, 101]]] + exp_nms_scores = [[.95, .9, .85, .3]] + exp_nms_classes = [[0, 0, 1, 0]] + def graph_fn(boxes, scores): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, + max_total_size=max_output_size) + self.assertIsNone(nmsed_masks) + self.assertIsNone(nmsed_additional_fields) + return (nmsed_boxes, nmsed_scores, nmsed_classes, num_detections) + + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertEqual(num_detections, [4]) + + def test_batch_iou_with_negative_data(self): + def graph_fn(): + boxes = tf.constant([[[0, -0.01, 0.1, 1.1], [0, 0.2, 0.2, 5.0], + [0, -0.01, 0.1, 1.], [-1, -1, -1, -1]]], tf.float32) + iou = post_processing.batch_iou(boxes, boxes) + return iou + iou = self.execute_cpu(graph_fn, []) + expected_iou = [[[0.99999994, 0.0917431, 0.9099099, -1.], + [0.0917431, 1., 0.08154944, -1.], + [0.9099099, 0.08154944, 1., -1.], [-1., -1., -1., -1.]]] + self.assertAllClose(iou, expected_iou) + + @parameterized.parameters(False, True) + def test_batch_multiclass_nms_with_batch_size_2(self, use_dynamic_map_fn): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = np.array([[[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 999, 2, 1004], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101], + [0, 0, 0, 0]]]) + exp_nms_scores = np.array([[.95, .9, 0, 0], + [.85, .5, .3, 0]]) + exp_nms_classes = np.array([[0, 0, 0, 0], + [1, 0, 0, 0]]) + def graph_fn(boxes, scores): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, + max_total_size=max_output_size, + use_dynamic_map_fn=use_dynamic_map_fn) + self.assertIsNone(nmsed_masks) + self.assertIsNone(nmsed_additional_fields) + # Check static shapes + self.assertAllEqual(nmsed_boxes.shape.as_list(), + exp_nms_corners.shape) + self.assertAllEqual(nmsed_scores.shape.as_list(), + exp_nms_scores.shape) + self.assertAllEqual(nmsed_classes.shape.as_list(), + exp_nms_classes.shape) + self.assertEqual(num_detections.shape.as_list(), [2]) + return (nmsed_boxes, nmsed_scores, nmsed_classes, num_detections) + + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(num_detections, [2, 3]) + + def test_batch_multiclass_nms_with_per_batch_clip_window(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + clip_window = np.array([0., 0., 200., 200.], np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = np.array([[[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 10.1, 1, 11.1], + [0, 100, 1, 101], + [0, 0, 0, 0], + [0, 0, 0, 0]]]) + exp_nms_scores = np.array([[.95, .9, 0, 0], + [.5, .3, 0, 0]]) + exp_nms_classes = np.array([[0, 0, 0, 0], + [0, 0, 0, 0]]) + def graph_fn(boxes, scores, clip_window): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + clip_window=clip_window) + self.assertIsNone(nmsed_masks) + self.assertIsNone(nmsed_additional_fields) + # Check static shapes + self.assertAllEqual(nmsed_boxes.shape.as_list(), + exp_nms_corners.shape) + self.assertAllEqual(nmsed_scores.shape.as_list(), + exp_nms_scores.shape) + self.assertAllEqual(nmsed_classes.shape.as_list(), + exp_nms_classes.shape) + self.assertEqual(num_detections.shape.as_list(), [2]) + return nmsed_boxes, nmsed_scores, nmsed_classes, num_detections + + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores, clip_window]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(num_detections, [2, 2]) + + def test_batch_multiclass_nms_with_per_image_clip_window(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + clip_window = np.array([[0., 0., 5., 5.], + [0., 0., 200., 200.]], np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = np.array([[[0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 10.1, 1, 11.1], + [0, 100, 1, 101], + [0, 0, 0, 0], + [0, 0, 0, 0]]]) + exp_nms_scores = np.array([[.9, 0., 0., 0.], + [.5, .3, 0, 0]]) + exp_nms_classes = np.array([[0, 0, 0, 0], + [0, 0, 0, 0]]) + + def graph_fn(boxes, scores, clip_window): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + clip_window=clip_window) + self.assertIsNone(nmsed_masks) + self.assertIsNone(nmsed_additional_fields) + # Check static shapes + self.assertAllEqual(nmsed_boxes.shape.as_list(), + exp_nms_corners.shape) + self.assertAllEqual(nmsed_scores.shape.as_list(), + exp_nms_scores.shape) + self.assertAllEqual(nmsed_classes.shape.as_list(), + exp_nms_classes.shape) + self.assertEqual(num_detections.shape.as_list(), [2]) + return nmsed_boxes, nmsed_scores, nmsed_classes, num_detections + + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores, clip_window]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(num_detections, [1, 2]) + + def test_batch_multiclass_nms_with_masks(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + masks = np.array([[[[[0, 1], [2, 3]], [[1, 2], [3, 4]]], + [[[2, 3], [4, 5]], [[3, 4], [5, 6]]], + [[[4, 5], [6, 7]], [[5, 6], [7, 8]]], + [[[6, 7], [8, 9]], [[7, 8], [9, 10]]]], + [[[[8, 9], [10, 11]], [[9, 10], [11, 12]]], + [[[10, 11], [12, 13]], [[11, 12], [13, 14]]], + [[[12, 13], [14, 15]], [[13, 14], [15, 16]]], + [[[14, 15], [16, 17]], [[15, 16], [17, 18]]]]], + np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = np.array([[[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 999, 2, 1004], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101], + [0, 0, 0, 0]]]) + exp_nms_scores = np.array([[.95, .9, 0, 0], + [.85, .5, .3, 0]]) + exp_nms_classes = np.array([[0, 0, 0, 0], + [1, 0, 0, 0]]) + exp_nms_masks = np.array([[[[6, 7], [8, 9]], + [[0, 1], [2, 3]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]], + [[[13, 14], [15, 16]], + [[8, 9], [10, 11]], + [[10, 11], [12, 13]], + [[0, 0], [0, 0]]]]) + + def graph_fn(boxes, scores, masks): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + masks=masks) + self.assertIsNone(nmsed_additional_fields) + # Check static shapes + self.assertAllEqual(nmsed_boxes.shape.as_list(), exp_nms_corners.shape) + self.assertAllEqual(nmsed_scores.shape.as_list(), exp_nms_scores.shape) + self.assertAllEqual(nmsed_classes.shape.as_list(), exp_nms_classes.shape) + self.assertAllEqual(nmsed_masks.shape.as_list(), exp_nms_masks.shape) + self.assertEqual(num_detections.shape.as_list(), [2]) + return (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + num_detections) + + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores, masks]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(num_detections, [2, 3]) + self.assertAllClose(nmsed_masks, exp_nms_masks) + + def test_batch_multiclass_nms_with_additional_fields(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + keypoints = np.array( + [[[[6, 7], [8, 9]], + [[0, 1], [2, 3]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]], + [[[13, 14], [15, 16]], + [[8, 9], [10, 11]], + [[10, 11], [12, 13]], + [[0, 0], [0, 0]]]], + np.float32) + size = np.array( + [[[[6], [8]], [[0], [2]], [[0], [0]], [[0], [0]]], + [[[13], [15]], [[8], [10]], [[10], [12]], [[0], [0]]]], np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = np.array([[[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 999, 2, 1004], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101], + [0, 0, 0, 0]]]) + exp_nms_scores = np.array([[.95, .9, 0, 0], + [.85, .5, .3, 0]]) + exp_nms_classes = np.array([[0, 0, 0, 0], + [1, 0, 0, 0]]) + exp_nms_additional_fields = { + 'keypoints': np.array([[[[0, 0], [0, 0]], + [[6, 7], [8, 9]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]], + [[[10, 11], [12, 13]], + [[13, 14], [15, 16]], + [[8, 9], [10, 11]], + [[0, 0], [0, 0]]]]) + } + exp_nms_additional_fields['size'] = np.array([[[[0], [0]], [[6], [8]], + [[0], [0]], [[0], [0]]], + [[[10], [12]], [[13], [15]], + [[8], [10]], [[0], [0]]]]) + + def graph_fn(boxes, scores, keypoints, size): + additional_fields = {'keypoints': keypoints, 'size': size} + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + additional_fields=additional_fields) + self.assertIsNone(nmsed_masks) + # Check static shapes + self.assertAllEqual(nmsed_boxes.shape.as_list(), exp_nms_corners.shape) + self.assertAllEqual(nmsed_scores.shape.as_list(), exp_nms_scores.shape) + self.assertAllEqual(nmsed_classes.shape.as_list(), exp_nms_classes.shape) + self.assertEqual(len(nmsed_additional_fields), + len(exp_nms_additional_fields)) + for key in exp_nms_additional_fields: + self.assertAllEqual(nmsed_additional_fields[key].shape.as_list(), + exp_nms_additional_fields[key].shape) + self.assertEqual(num_detections.shape.as_list(), [2]) + return (nmsed_boxes, nmsed_scores, nmsed_classes, + nmsed_additional_fields['keypoints'], + nmsed_additional_fields['size'], + num_detections) + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_keypoints, nmsed_size, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores, keypoints, + size]) + + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(nmsed_keypoints, + exp_nms_additional_fields['keypoints']) + self.assertAllClose(nmsed_size, + exp_nms_additional_fields['size']) + self.assertAllClose(num_detections, [2, 3]) + + def test_batch_multiclass_nms_with_masks_and_num_valid_boxes(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + masks = np.array([[[[[0, 1], [2, 3]], [[1, 2], [3, 4]]], + [[[2, 3], [4, 5]], [[3, 4], [5, 6]]], + [[[4, 5], [6, 7]], [[5, 6], [7, 8]]], + [[[6, 7], [8, 9]], [[7, 8], [9, 10]]]], + [[[[8, 9], [10, 11]], [[9, 10], [11, 12]]], + [[[10, 11], [12, 13]], [[11, 12], [13, 14]]], + [[[12, 13], [14, 15]], [[13, 14], [15, 16]]], + [[[14, 15], [16, 17]], [[15, 16], [17, 18]]]]], + np.float32) + num_valid_boxes = np.array([1, 1], np.int32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = [[[0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 10.1, 1, 11.1], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_nms_scores = [[.9, 0, 0, 0], + [.5, 0, 0, 0]] + exp_nms_classes = [[0, 0, 0, 0], + [0, 0, 0, 0]] + exp_nms_masks = [[[[0, 1], [2, 3]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]], + [[[8, 9], [10, 11]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]]] + + def graph_fn(boxes, scores, masks, num_valid_boxes): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + masks=masks, num_valid_boxes=num_valid_boxes) + self.assertIsNone(nmsed_additional_fields) + return (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + num_detections) + + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores, masks, + num_valid_boxes]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(num_detections, [1, 1]) + self.assertAllClose(nmsed_masks, exp_nms_masks) + + def test_batch_multiclass_nms_with_additional_fields_and_num_valid_boxes( + self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], np.float32) + keypoints = np.array( + [[[[6, 7], [8, 9]], + [[0, 1], [2, 3]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]], + [[[13, 14], [15, 16]], + [[8, 9], [10, 11]], + [[10, 11], [12, 13]], + [[0, 0], [0, 0]]]], + np.float32) + size = np.array( + [[[[7], [9]], [[1], [3]], [[0], [0]], [[0], [0]]], + [[[14], [16]], [[9], [11]], [[11], [13]], [[0], [0]]]], np.float32) + + num_valid_boxes = np.array([1, 1], np.int32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = [[[0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 10.1, 1, 11.1], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_nms_scores = [[.9, 0, 0, 0], + [.5, 0, 0, 0]] + exp_nms_classes = [[0, 0, 0, 0], + [0, 0, 0, 0]] + exp_nms_additional_fields = { + 'keypoints': np.array([[[[6, 7], [8, 9]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]], + [[[13, 14], [15, 16]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]], + [[0, 0], [0, 0]]]]) + } + + exp_nms_additional_fields['size'] = np.array([[[[7], [9]], [[0], [0]], + [[0], [0]], [[0], [0]]], + [[[14], [16]], [[0], [0]], + [[0], [0]], [[0], [0]]]]) + def graph_fn(boxes, scores, keypoints, size, num_valid_boxes): + additional_fields = {'keypoints': keypoints, 'size': size} + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + num_valid_boxes=num_valid_boxes, + additional_fields=additional_fields) + self.assertIsNone(nmsed_masks) + return (nmsed_boxes, nmsed_scores, nmsed_classes, + nmsed_additional_fields['keypoints'], + nmsed_additional_fields['size'], num_detections) + + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_keypoints, nmsed_size, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores, keypoints, + size, num_valid_boxes]) + + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertAllClose(nmsed_keypoints, + exp_nms_additional_fields['keypoints']) + self.assertAllClose(nmsed_size, + exp_nms_additional_fields['size']) + self.assertAllClose(num_detections, [1, 1]) + + def test_combined_nms_with_batch_size_2(self): + """Test use_combined_nms.""" + boxes = np.array([[[[0, 0, 0.1, 0.1], [0, 0, 0.1, 0.1]], + [[0, 0.01, 1, 0.11], [0, 0.6, 0.1, 0.7]], + [[0, -0.01, 0.1, 0.09], [0, -0.1, 0.1, 0.09]], + [[0, 0.11, 0.1, 0.2], [0, 0.11, 0.1, 0.2]]], + [[[0, 0, 0.2, 0.2], [0, 0, 0.2, 0.2]], + [[0, 0.02, 0.2, 0.22], [0, 0.02, 0.2, 0.22]], + [[0, -0.02, 0.2, 0.19], [0, -0.02, 0.2, 0.19]], + [[0, 0.21, 0.2, 0.3], [0, 0.21, 0.2, 0.3]]]], + np.float32) + scores = np.array([[[.1, 0.9], [.75, 0.8], + [.6, 0.3], [0.95, 0.1]], + [[.1, 0.9], [.75, 0.8], + [.6, .3], [.95, .1]]], np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 3 + + exp_nms_corners = np.array([[[0, 0.11, 0.1, 0.2], + [0, 0, 0.1, 0.1], + [0, 0.6, 0.1, 0.7]], + [[0, 0.21, 0.2, 0.3], + [0, 0, 0.2, 0.2], + [0, 0.02, 0.2, 0.22]]]) + exp_nms_scores = np.array([[.95, .9, 0.8], + [.95, .9, .75]]) + exp_nms_classes = np.array([[0, 1, 1], + [0, 1, 0]]) + + def graph_fn(boxes, scores): + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, max_total_size=max_output_size, + use_static_shapes=True, + use_combined_nms=True) + self.assertIsNone(nmsed_masks) + self.assertIsNone(nmsed_additional_fields) + return (nmsed_boxes, nmsed_scores, nmsed_classes, num_detections) + + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute_cpu(graph_fn, [boxes, scores]) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertListEqual(num_detections.tolist(), [3, 3]) + + def test_batch_multiclass_nms_with_use_static_shapes(self): + boxes = np.array([[[[0, 0, 1, 1], [0, 0, 4, 5]], + [[0, 0.1, 1, 1.1], [0, 0.1, 2, 1.1]], + [[0, -0.1, 1, 0.9], [0, -0.1, 1, 0.9]], + [[0, 10, 1, 11], [0, 10, 1, 11]]], + [[[0, 10.1, 1, 11.1], [0, 10.1, 1, 11.1]], + [[0, 100, 1, 101], [0, 100, 1, 101]], + [[0, 1000, 1, 1002], [0, 999, 2, 1004]], + [[0, 1000, 1, 1002.1], [0, 999, 2, 1002.7]]]], + np.float32) + scores = np.array([[[.9, 0.01], [.75, 0.05], + [.6, 0.01], [.95, 0]], + [[.5, 0.01], [.3, 0.01], + [.01, .85], [.01, .5]]], + np.float32) + clip_window = np.array([[0., 0., 5., 5.], + [0., 0., 200., 200.]], + np.float32) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + + exp_nms_corners = np.array([[[0, 0, 1, 1], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]], + [[0, 10.1, 1, 11.1], + [0, 100, 1, 101], + [0, 0, 0, 0], + [0, 0, 0, 0]]]) + exp_nms_scores = np.array([[.9, 0., 0., 0.], + [.5, .3, 0, 0]]) + exp_nms_classes = np.array([[0, 0, 0, 0], + [0, 0, 0, 0]]) + + def graph_fn(boxes, scores, clip_window): + (nmsed_boxes, nmsed_scores, nmsed_classes, _, _, num_detections + ) = post_processing.batch_multiclass_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, + max_size_per_class=max_output_size, clip_window=clip_window, + use_static_shapes=True) + return nmsed_boxes, nmsed_scores, nmsed_classes, num_detections + + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute(graph_fn, [boxes, scores, clip_window]) + for i in range(len(num_detections)): + self.assertAllClose(nmsed_boxes[i, 0:num_detections[i]], + exp_nms_corners[i, 0:num_detections[i]]) + self.assertAllClose(nmsed_scores[i, 0:num_detections[i]], + exp_nms_scores[i, 0:num_detections[i]]) + self.assertAllClose(nmsed_classes[i, 0:num_detections[i]], + exp_nms_classes[i, 0:num_detections[i]]) + self.assertAllClose(num_detections, [1, 2]) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/batcher.py b/workspace/virtuallab/object_detection/core/batcher.py new file mode 100644 index 0000000..26832e3 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/batcher.py @@ -0,0 +1,141 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Provides functions to batch a dictionary of input tensors.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections + +from six.moves import range +import tensorflow.compat.v1 as tf + +from object_detection.core import prefetcher + +rt_shape_str = '_runtime_shapes' + + +class BatchQueue(object): + """BatchQueue class. + + This class creates a batch queue to asynchronously enqueue tensors_dict. + It also adds a FIFO prefetcher so that the batches are readily available + for the consumers. Dequeue ops for a BatchQueue object can be created via + the Dequeue method which evaluates to a batch of tensor_dict. + + Example input pipeline with batching: + ------------------------------------ + key, string_tensor = slim.parallel_reader.parallel_read(...) + tensor_dict = decoder.decode(string_tensor) + tensor_dict = preprocessor.preprocess(tensor_dict, ...) + batch_queue = batcher.BatchQueue(tensor_dict, + batch_size=32, + batch_queue_capacity=2000, + num_batch_queue_threads=8, + prefetch_queue_capacity=20) + tensor_dict = batch_queue.dequeue() + outputs = Model(tensor_dict) + ... + ----------------------------------- + + Notes: + ----- + This class batches tensors of unequal sizes by zero padding and unpadding + them after generating a batch. This can be computationally expensive when + batching tensors (such as images) that are of vastly different sizes. So it is + recommended that the shapes of such tensors be fully defined in tensor_dict + while other lightweight tensors such as bounding box corners and class labels + can be of varying sizes. Use either crop or resize operations to fully define + the shape of an image in tensor_dict. + + It is also recommended to perform any preprocessing operations on tensors + before passing to BatchQueue and subsequently calling the Dequeue method. + + Another caveat is that this class does not read the last batch if it is not + full. The current implementation makes it hard to support that use case. So, + for evaluation, when it is critical to run all the examples through your + network use the input pipeline example mentioned in core/prefetcher.py. + """ + + def __init__(self, tensor_dict, batch_size, batch_queue_capacity, + num_batch_queue_threads, prefetch_queue_capacity): + """Constructs a batch queue holding tensor_dict. + + Args: + tensor_dict: dictionary of tensors to batch. + batch_size: batch size. + batch_queue_capacity: max capacity of the queue from which the tensors are + batched. + num_batch_queue_threads: number of threads to use for batching. + prefetch_queue_capacity: max capacity of the queue used to prefetch + assembled batches. + """ + # Remember static shapes to set shapes of batched tensors. + static_shapes = collections.OrderedDict( + {key: tensor.get_shape() for key, tensor in tensor_dict.items()}) + # Remember runtime shapes to unpad tensors after batching. + runtime_shapes = collections.OrderedDict( + {(key + rt_shape_str): tf.shape(tensor) + for key, tensor in tensor_dict.items()}) + + all_tensors = tensor_dict + all_tensors.update(runtime_shapes) + batched_tensors = tf.train.batch( + all_tensors, + capacity=batch_queue_capacity, + batch_size=batch_size, + dynamic_pad=True, + num_threads=num_batch_queue_threads) + + self._queue = prefetcher.prefetch(batched_tensors, + prefetch_queue_capacity) + self._static_shapes = static_shapes + self._batch_size = batch_size + + def dequeue(self): + """Dequeues a batch of tensor_dict from the BatchQueue. + + TODO: use allow_smaller_final_batch to allow running over the whole eval set + + Returns: + A list of tensor_dicts of the requested batch_size. + """ + batched_tensors = self._queue.dequeue() + # Separate input tensors from tensors containing their runtime shapes. + tensors = {} + shapes = {} + for key, batched_tensor in batched_tensors.items(): + unbatched_tensor_list = tf.unstack(batched_tensor) + for i, unbatched_tensor in enumerate(unbatched_tensor_list): + if rt_shape_str in key: + shapes[(key[:-len(rt_shape_str)], i)] = unbatched_tensor + else: + tensors[(key, i)] = unbatched_tensor + + # Undo that padding using shapes and create a list of size `batch_size` that + # contains tensor dictionaries. + tensor_dict_list = [] + batch_size = self._batch_size + for batch_id in range(batch_size): + tensor_dict = {} + for key in self._static_shapes: + tensor_dict[key] = tf.slice(tensors[(key, batch_id)], + tf.zeros_like(shapes[(key, batch_id)]), + shapes[(key, batch_id)]) + tensor_dict[key].set_shape(self._static_shapes[key]) + tensor_dict_list.append(tensor_dict) + + return tensor_dict_list diff --git a/workspace/virtuallab/object_detection/core/batcher.pyc b/workspace/virtuallab/object_detection/core/batcher.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31c60a8a73fc4dcb89ccc27fc915715e59edfbbc GIT binary patch literal 5358 zcmb_gTaz0{74Fe$wRhV|0J9{7uA~5^%4qHI0$bh`b{129h#khJ3<5KZW?HM!&P>bQ zJ=#@jw~8tQkGzDZ@XkB0{0gf0Df|Jxb9zQAtqIv8(ORv(oIdwkd*$Cdn}7fFXHg>R z&kBBj^1DFTzhIII(Z?8wv?cOD6fMzjiGbECyj~Ig6~Autx-I%`zg`vjs_3us-HymR zqF59CwfWh)7;lL2rs!`m#G1&rMX@9LJAhah{c9rK5bC-Zg3_k=gAgx;_*MUgNLR&^ z?iLuc`xrhpdYYxm%3)O|F4LuzPRl{;l97yMn(ODjR!%A>ohq$1wwK|y=mr?v zbqw*q>b!C)%8H3L&L6DfAkC(k1!BK*4LcK)l`d)sKJo{vI52T}r2KM|;nYxp5oMS{ zWUA6-g^KJbo+#@GG?!%B)|?#HiRoPjRiB7ethF728>vjEpXIW%HR-Wqsn+6p;#hK=shc zIJW>#Q$q6K;e*F=Vj2M@PTVMlc1Ow+AW}w2P)#$Sji+&z$Aeq})leITOJJ?4P_TuR zhsu-H6POa`vAj@?)Pu1~oJ`_U4pc2kDyLZtWJIvJTqrlvsXQ5Fu)dncdBq}=khRqC z0&o9XE+F{nXPBJ$;c>nh8+gG|>RzdTl|w$UFw;jcQog7$I)b;s&9b3A9OaRa&d==T?bOMLWrih1;DuIicq3aDdU0YGAwK6N>BW2$MHKYLw zeegMrtpk=c8xECGrSlB;C#1}_Ar~>uR7+WugtVf_w$Naqg7JR4OO#>XgHx zm>J2kYE{VaJ8|fCQn=|f` zhG1o7kRV~uaz+Wbj(B2ifwFECLqZG`E-1K%wQM=Pm|IC`TMaBZc1gxcV0Z?5C9yJN z_e!tbB%Y$65R-M@pI4ex!zTg7xt}FQJ#yZ2WQH=#w*UavNIm3%$m*404?+3O75u?F zp)BHKV8fMBY~UaoyiTY-d#V+zki=GzM^W5U<*4&MJopwnZA~vRnady>;XomVYB2>} z<Xuf`ut%{c|aVA8xDb4_Z$rfFq$+k$> z&}dNyWdDku?l&+Ge?}$sCF!z?q6!ln&^gMIJ=O} z=);SMVrsv39-1$oE?19zxC_F$t|b&=WI4~~(CC8F1CkYOHbjxAYH~F}l{OSUr^4(@ z91IZmjGl`Q`4ssHxnGkY8!U}GT>xi&davS8uM)8AJf90zFjfsVFpgq^N{i#ua?q z_&y;A`z|KvZ(+yhZ;Icv#5vprW^IWvx6$yPPg~+e2lfSE-2`u7cBGu|PPwmiEo}8} zJ3@`N#*rU;z4>cc3VR2`-bfc}Z>$G<1yw@3=Nq;tRSta{!efskgH^*}VJQ^uQfrCi zKl$L7gouc3h`B7(PqQqyFsWsE=Ry`AK9C5m4{^Zi%_WhDXXf7x9iifi;<7&!Gw`9gm`Ky71D&cYg~@6 zdzW?ej@pbP|HRg3H3s5XT5v#3(AmOF>Z!$4B7OxFSw}Po7>Eisy!7p)Jml z-dDsJsUFFkOULO`vNrNLmWR(DFx(oI3m6qO;J72qpQuQnLSTv7fPH{&NfeS{OWb|& zF`pt|0O@?|*^91tb_7OHHo)MzINubfp9+WNrl<5ralXZ@Zi~~5fi^+^x8fW`z@e`! zK)FR!c4=$7=AqsK6P~AL4F&@CcPwIeLss87fzqa$Xbfp+!Yww9F9q_k_SuM`m4MG;yJT&5?KyB_jTN<%H>TE zw9ZjI%iN9sa?YCz=3{uDU?_|jS(K&W+gNpyN8UuC=p5_=woS^i3dXPw{mTK<51$C5;3 S9Q?JmTQ`E&TQ^#rmHz;<`?tdY literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/batcher_tf1_test.py b/workspace/virtuallab/object_detection/core/batcher_tf1_test.py new file mode 100644 index 0000000..1688b87 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/batcher_tf1_test.py @@ -0,0 +1,165 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.batcher.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import unittest +import numpy as np +from six.moves import range +import tensorflow.compat.v1 as tf +import tf_slim as slim + +from object_detection.core import batcher +from object_detection.utils import tf_version + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only test.') +class BatcherTest(tf.test.TestCase): + + def test_batch_and_unpad_2d_tensors_of_different_sizes_in_1st_dimension(self): + with self.test_session() as sess: + batch_size = 3 + num_batches = 2 + examples = tf.Variable(tf.constant(2, dtype=tf.int32)) + counter = examples.count_up_to(num_batches * batch_size + 2) + boxes = tf.tile( + tf.reshape(tf.range(4), [1, 4]), tf.stack([counter, tf.constant(1)])) + batch_queue = batcher.BatchQueue( + tensor_dict={'boxes': boxes}, + batch_size=batch_size, + batch_queue_capacity=100, + num_batch_queue_threads=1, + prefetch_queue_capacity=100) + batch = batch_queue.dequeue() + + for tensor_dict in batch: + for tensor in tensor_dict.values(): + self.assertAllEqual([None, 4], tensor.get_shape().as_list()) + + tf.initialize_all_variables().run() + with slim.queues.QueueRunners(sess): + i = 2 + for _ in range(num_batches): + batch_np = sess.run(batch) + for tensor_dict in batch_np: + for tensor in tensor_dict.values(): + self.assertAllEqual(tensor, np.tile(np.arange(4), (i, 1))) + i += 1 + with self.assertRaises(tf.errors.OutOfRangeError): + sess.run(batch) + + def test_batch_and_unpad_2d_tensors_of_different_sizes_in_all_dimensions( + self): + with self.test_session() as sess: + batch_size = 3 + num_batches = 2 + examples = tf.Variable(tf.constant(2, dtype=tf.int32)) + counter = examples.count_up_to(num_batches * batch_size + 2) + image = tf.reshape( + tf.range(counter * counter), tf.stack([counter, counter])) + batch_queue = batcher.BatchQueue( + tensor_dict={'image': image}, + batch_size=batch_size, + batch_queue_capacity=100, + num_batch_queue_threads=1, + prefetch_queue_capacity=100) + batch = batch_queue.dequeue() + + for tensor_dict in batch: + for tensor in tensor_dict.values(): + self.assertAllEqual([None, None], tensor.get_shape().as_list()) + + tf.initialize_all_variables().run() + with slim.queues.QueueRunners(sess): + i = 2 + for _ in range(num_batches): + batch_np = sess.run(batch) + for tensor_dict in batch_np: + for tensor in tensor_dict.values(): + self.assertAllEqual(tensor, np.arange(i * i).reshape((i, i))) + i += 1 + with self.assertRaises(tf.errors.OutOfRangeError): + sess.run(batch) + + def test_batch_and_unpad_2d_tensors_of_same_size_in_all_dimensions(self): + with self.test_session() as sess: + batch_size = 3 + num_batches = 2 + examples = tf.Variable(tf.constant(1, dtype=tf.int32)) + counter = examples.count_up_to(num_batches * batch_size + 1) + image = tf.reshape(tf.range(1, 13), [4, 3]) * counter + batch_queue = batcher.BatchQueue( + tensor_dict={'image': image}, + batch_size=batch_size, + batch_queue_capacity=100, + num_batch_queue_threads=1, + prefetch_queue_capacity=100) + batch = batch_queue.dequeue() + + for tensor_dict in batch: + for tensor in tensor_dict.values(): + self.assertAllEqual([4, 3], tensor.get_shape().as_list()) + + tf.initialize_all_variables().run() + with slim.queues.QueueRunners(sess): + i = 1 + for _ in range(num_batches): + batch_np = sess.run(batch) + for tensor_dict in batch_np: + for tensor in tensor_dict.values(): + self.assertAllEqual(tensor, np.arange(1, 13).reshape((4, 3)) * i) + i += 1 + with self.assertRaises(tf.errors.OutOfRangeError): + sess.run(batch) + + def test_batcher_when_batch_size_is_one(self): + with self.test_session() as sess: + batch_size = 1 + num_batches = 2 + examples = tf.Variable(tf.constant(2, dtype=tf.int32)) + counter = examples.count_up_to(num_batches * batch_size + 2) + image = tf.reshape( + tf.range(counter * counter), tf.stack([counter, counter])) + batch_queue = batcher.BatchQueue( + tensor_dict={'image': image}, + batch_size=batch_size, + batch_queue_capacity=100, + num_batch_queue_threads=1, + prefetch_queue_capacity=100) + batch = batch_queue.dequeue() + + for tensor_dict in batch: + for tensor in tensor_dict.values(): + self.assertAllEqual([None, None], tensor.get_shape().as_list()) + + tf.initialize_all_variables().run() + with slim.queues.QueueRunners(sess): + i = 2 + for _ in range(num_batches): + batch_np = sess.run(batch) + for tensor_dict in batch_np: + for tensor in tensor_dict.values(): + self.assertAllEqual(tensor, np.arange(i * i).reshape((i, i))) + i += 1 + with self.assertRaises(tf.errors.OutOfRangeError): + sess.run(batch) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/box_coder.py b/workspace/virtuallab/object_detection/core/box_coder.py new file mode 100644 index 0000000..c6e54a4 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/box_coder.py @@ -0,0 +1,158 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Base box coder. + +Box coders convert between coordinate frames, namely image-centric +(with (0,0) on the top left of image) and anchor-centric (with (0,0) being +defined by a specific anchor). + +Users of a BoxCoder can call two methods: + encode: which encodes a box with respect to a given anchor + (or rather, a tensor of boxes wrt a corresponding tensor of anchors) and + decode: which inverts this encoding with a decode operation. +In both cases, the arguments are assumed to be in 1-1 correspondence already; +it is not the job of a BoxCoder to perform matching. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from abc import ABCMeta +from abc import abstractmethod +from abc import abstractproperty + +import six +import tensorflow.compat.v1 as tf + +from object_detection.utils import shape_utils + + +# Box coder types. +FASTER_RCNN = 'faster_rcnn' +KEYPOINT = 'keypoint' +MEAN_STDDEV = 'mean_stddev' +SQUARE = 'square' + + +class BoxCoder(six.with_metaclass(ABCMeta, object)): + """Abstract base class for box coder.""" + + @abstractproperty + def code_size(self): + """Return the size of each code. + + This number is a constant and should agree with the output of the `encode` + op (e.g. if rel_codes is the output of self.encode(...), then it should have + shape [N, code_size()]). This abstractproperty should be overridden by + implementations. + + Returns: + an integer constant + """ + pass + + def encode(self, boxes, anchors): + """Encode a box list relative to an anchor collection. + + Args: + boxes: BoxList holding N boxes to be encoded + anchors: BoxList of N anchors + + Returns: + a tensor representing N relative-encoded boxes + """ + with tf.name_scope('Encode'): + return self._encode(boxes, anchors) + + def decode(self, rel_codes, anchors): + """Decode boxes that are encoded relative to an anchor collection. + + Args: + rel_codes: a tensor representing N relative-encoded boxes + anchors: BoxList of anchors + + Returns: + boxlist: BoxList holding N boxes encoded in the ordinary way (i.e., + with corners y_min, x_min, y_max, x_max) + """ + with tf.name_scope('Decode'): + return self._decode(rel_codes, anchors) + + @abstractmethod + def _encode(self, boxes, anchors): + """Method to be overriden by implementations. + + Args: + boxes: BoxList holding N boxes to be encoded + anchors: BoxList of N anchors + + Returns: + a tensor representing N relative-encoded boxes + """ + pass + + @abstractmethod + def _decode(self, rel_codes, anchors): + """Method to be overriden by implementations. + + Args: + rel_codes: a tensor representing N relative-encoded boxes + anchors: BoxList of anchors + + Returns: + boxlist: BoxList holding N boxes encoded in the ordinary way (i.e., + with corners y_min, x_min, y_max, x_max) + """ + pass + + +def batch_decode(encoded_boxes, box_coder, anchors): + """Decode a batch of encoded boxes. + + This op takes a batch of encoded bounding boxes and transforms + them to a batch of bounding boxes specified by their corners in + the order of [y_min, x_min, y_max, x_max]. + + Args: + encoded_boxes: a float32 tensor of shape [batch_size, num_anchors, + code_size] representing the location of the objects. + box_coder: a BoxCoder object. + anchors: a BoxList of anchors used to encode `encoded_boxes`. + + Returns: + decoded_boxes: a float32 tensor of shape [batch_size, num_anchors, + coder_size] representing the corners of the objects in the order + of [y_min, x_min, y_max, x_max]. + + Raises: + ValueError: if batch sizes of the inputs are inconsistent, or if + the number of anchors inferred from encoded_boxes and anchors are + inconsistent. + """ + encoded_boxes.get_shape().assert_has_rank(3) + if (shape_utils.get_dim_as_int(encoded_boxes.get_shape()[1]) + != anchors.num_boxes_static()): + raise ValueError('The number of anchors inferred from encoded_boxes' + ' and anchors are inconsistent: shape[1] of encoded_boxes' + ' %s should be equal to the number of anchors: %s.' % + (shape_utils.get_dim_as_int(encoded_boxes.get_shape()[1]), + anchors.num_boxes_static())) + + decoded_boxes = tf.stack([ + box_coder.decode(boxes, anchors).get() + for boxes in tf.unstack(encoded_boxes) + ]) + return decoded_boxes diff --git a/workspace/virtuallab/object_detection/core/box_coder.pyc b/workspace/virtuallab/object_detection/core/box_coder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfc7b25a4c2d9062fbcc9c79be5919f75b2bd156 GIT binary patch literal 5526 zcmds5TW{mW6&^~mY|Gc|Cf*h;ft$bvv%} zcu{l~?Rb&LOQN%6$4eq!7M*3Lh4+RSu885P=&bR~vWVA3vLQMf*xeAFO%bk$&MP9s zj(Z|p6P^1aTo-Ct3||$hF4Sw{+luIc&kgYxA-)yji_Vq^H)+s$Ai^ba(tHK->Ai{& zhrU*_n_bBu3sv4~G!Dymjj8lP?nwOCo$mUx+Z*9vgm1dx%$wo3(JtMQ8-MlURG{h$uWO;c|&L8ZmDD5{w)r(RU z%I-w^Qjb&+^`f9SwheAS(JI$;sV~9!5y>C}AFTRuEX`#m6J-WjsP`LEr6l6Myc|Tq zpcrYKBjK6hToC~SUNPH`E+DbxuOVeK%Vq9El6(h?Mx`1PAdACF%S$N9hkA0lB1@s8 z{@fmWiDro#GF0bBsX;*c0c<4=3+6A|pXH%^)%^8fM++6p2efx|ijNOnegz zz*?&jQK&Q>Jj@0jephGl*eEYbMp6MM8qK>!V|nG1Q5as&x%l03QmeIhvFU> zLE*JP;6N`3!$lF+$_-0ATquXj!aZp+={IogK_Rc~QZNQFj3}Y?xv&gK-{t3s*r{Ox z^iP_^p#A`Bt}^4?Mw^blR%A=%!_&##5GfLWr{rbnIO!rv@J241YU8KyUIe2aWaAhy z+0T`-E=X>Xjm>DxaZ96THj19{b%^PvYV}((>d9Qi9#PVC@y&Bu#l4n2*KD;~+w8Zg zg!dH`2mXcP3)zbDX?us6_DGE8_Str;;JoyU(*EUnczOn)$|G150W#sc;E%DQz~Io) zg$AsW2oCrUNCBr%eL{^w6}-}JLaT+Blcq@oJ4N&Q9XxgiS)z6kG`k7Jj`eQV9RfYP zP#Ju|!FPcNY8L>(ddXX(iCM-uQW8t{F5N`d6RSZLPx??euz7!l7bCRgidDWK)nX3_C6NVz> zYft%oR1mO4nG6T3m9Whs8&aR?nFhWn6L~rSG8odLXOHtXca^p+;K7pddI8FJU{nfY zOY8`59GIf9>D-M?_Yv5SIb)Q{ANYt?AbqKx7qIU%R`=m3MK1g4R*?#L_qg^_>7BmZi9 z<}g-$)?r*qSuW)g>m)WA&p8Q2=u|!KeT?qNLoB6_aG~{(l{pU~V7~>_|A}cNMwBl_ zIxj$uF%DJKZTR-T5XJ?#5@vB^y7%bqZglNZVf!UM7Fk!OQTPKoeu(*((ecz*{@+9C zdq#V3heq4NUFTDytu_hIxg7I6hINk--t&-Y$FcG}8@yha1)k>;uDfgWc$FSMqX*?| zb_JKNo`o>Ky=eM&sjz$-A5HnB?$j$A>&t7)>lUr=*x=u>QA)ko0_<;p@85bX9po!BpVbERPl2W$t49^Flu$UTHrDg z#h>xdQSaTlb!?j;K7)J#1k3$YQ!Al?%mDeswpmju-R#DZqSdpVAGSwSpjpt5QhS3$ zGm5$mg~rn#&I4!6@C+4;Oye#)TTX+;QR+39igU(=zT1 z70X-!L-{Mt0xmj(e5ZH73*Ar;i6(zjvA8!XEt`0X^_P`6XNBA%a%r5K5>o@p3Ls0d^ztX!%k?Alx3)?&W%fUG#^q88<;G7}ZkgjlTlVH*iT;B*qW^Ni9Tnunoz&mfa@C?jf z6vqw0w+(N5^Vb+_N*?0DeP_$raO%}9XRA_os?M(~O-?Akz+#bvdSmq2NJ1_haLM{E zw;?2;-wj;)wh4$urJ;_l4Bc;QZ9vXtE69?OZ(0|7+|<|+w=)-eR+W^vo^L5z`~!zI z6Pfh)2Pdb;uIC=L+ng@{c>KjjAHLr{GvfM zzpnWFTLZ6JQk>%4H*xh$%`cTRnXTX0y#3o>;$Hna9(8Av{wwym>fEbr){sx>zxu`> F{tM3o@RtAp literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/box_coder_test.py b/workspace/virtuallab/object_detection/core/box_coder_test.py new file mode 100644 index 0000000..52765a9 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/box_coder_test.py @@ -0,0 +1,62 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.box_coder.""" +import tensorflow.compat.v1 as tf + +from object_detection.core import box_coder +from object_detection.core import box_list +from object_detection.utils import test_case + + +class MockBoxCoder(box_coder.BoxCoder): + """Test BoxCoder that encodes/decodes using the multiply-by-two function.""" + + def code_size(self): + return 4 + + def _encode(self, boxes, anchors): + return 2.0 * boxes.get() + + def _decode(self, rel_codes, anchors): + return box_list.BoxList(rel_codes / 2.0) + + +class BoxCoderTest(test_case.TestCase): + + def test_batch_decode(self): + + expected_boxes = [[[0.0, 0.1, 0.5, 0.6], [0.5, 0.6, 0.7, 0.8]], + [[0.1, 0.2, 0.3, 0.4], [0.7, 0.8, 0.9, 1.0]]] + + def graph_fn(): + mock_anchor_corners = tf.constant( + [[0, 0.1, 0.2, 0.3], [0.2, 0.4, 0.4, 0.6]], tf.float32) + mock_anchors = box_list.BoxList(mock_anchor_corners) + mock_box_coder = MockBoxCoder() + + encoded_boxes_list = [mock_box_coder.encode( + box_list.BoxList(tf.constant(boxes)), mock_anchors) + for boxes in expected_boxes] + encoded_boxes = tf.stack(encoded_boxes_list) + decoded_boxes = box_coder.batch_decode( + encoded_boxes, mock_box_coder, mock_anchors) + return decoded_boxes + decoded_boxes_result = self.execute(graph_fn, []) + self.assertAllClose(expected_boxes, decoded_boxes_result) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/box_list.py b/workspace/virtuallab/object_detection/core/box_list.py new file mode 100644 index 0000000..7b6b97e --- /dev/null +++ b/workspace/virtuallab/object_detection/core/box_list.py @@ -0,0 +1,210 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Bounding Box List definition. + +BoxList represents a list of bounding boxes as tensorflow +tensors, where each bounding box is represented as a row of 4 numbers, +[y_min, x_min, y_max, x_max]. It is assumed that all bounding boxes +within a given list correspond to a single image. See also +box_list_ops.py for common box related operations (such as area, iou, etc). + +Optionally, users can add additional related fields (such as weights). +We assume the following things to be true about fields: +* they correspond to boxes in the box_list along the 0th dimension +* they have inferrable rank at graph construction time +* all dimensions except for possibly the 0th can be inferred + (i.e., not None) at graph construction time. + +Some other notes: + * Following tensorflow conventions, we use height, width ordering, + and correspondingly, y,x (or ymin, xmin, ymax, xmax) ordering + * Tensors are always provided as (flat) [N, 4] tensors. +""" + +import tensorflow.compat.v1 as tf + +from object_detection.utils import shape_utils + + +class BoxList(object): + """Box collection.""" + + def __init__(self, boxes): + """Constructs box collection. + + Args: + boxes: a tensor of shape [N, 4] representing box corners + + Raises: + ValueError: if invalid dimensions for bbox data or if bbox data is not in + float32 format. + """ + if len(boxes.get_shape()) != 2 or boxes.get_shape()[-1] != 4: + raise ValueError('Invalid dimensions for box data: {}'.format( + boxes.shape)) + if boxes.dtype != tf.float32: + raise ValueError('Invalid tensor type: should be tf.float32') + self.data = {'boxes': boxes} + + def num_boxes(self): + """Returns number of boxes held in collection. + + Returns: + a tensor representing the number of boxes held in the collection. + """ + return tf.shape(self.data['boxes'])[0] + + def num_boxes_static(self): + """Returns number of boxes held in collection. + + This number is inferred at graph construction time rather than run-time. + + Returns: + Number of boxes held in collection (integer) or None if this is not + inferrable at graph construction time. + """ + return shape_utils.get_dim_as_int(self.data['boxes'].get_shape()[0]) + + def get_all_fields(self): + """Returns all fields.""" + return self.data.keys() + + def get_extra_fields(self): + """Returns all non-box fields (i.e., everything not named 'boxes').""" + return [k for k in self.data.keys() if k != 'boxes'] + + def add_field(self, field, field_data): + """Add field to box list. + + This method can be used to add related box data such as + weights/labels, etc. + + Args: + field: a string key to access the data via `get` + field_data: a tensor containing the data to store in the BoxList + """ + self.data[field] = field_data + + def has_field(self, field): + return field in self.data + + def get(self): + """Convenience function for accessing box coordinates. + + Returns: + a tensor with shape [N, 4] representing box coordinates. + """ + return self.get_field('boxes') + + def set(self, boxes): + """Convenience function for setting box coordinates. + + Args: + boxes: a tensor of shape [N, 4] representing box corners + + Raises: + ValueError: if invalid dimensions for bbox data + """ + if len(boxes.get_shape()) != 2 or boxes.get_shape()[-1] != 4: + raise ValueError('Invalid dimensions for box data.') + self.data['boxes'] = boxes + + def get_field(self, field): + """Accesses a box collection and associated fields. + + This function returns specified field with object; if no field is specified, + it returns the box coordinates. + + Args: + field: this optional string parameter can be used to specify + a related field to be accessed. + + Returns: + a tensor representing the box collection or an associated field. + + Raises: + ValueError: if invalid field + """ + if not self.has_field(field): + raise ValueError('field ' + str(field) + ' does not exist') + return self.data[field] + + def set_field(self, field, value): + """Sets the value of a field. + + Updates the field of a box_list with a given value. + + Args: + field: (string) name of the field to set value. + value: the value to assign to the field. + + Raises: + ValueError: if the box_list does not have specified field. + """ + if not self.has_field(field): + raise ValueError('field %s does not exist' % field) + self.data[field] = value + + def get_center_coordinates_and_sizes(self, scope=None): + """Computes the center coordinates, height and width of the boxes. + + Args: + scope: name scope of the function. + + Returns: + a list of 4 1-D tensors [ycenter, xcenter, height, width]. + """ + with tf.name_scope(scope, 'get_center_coordinates_and_sizes'): + box_corners = self.get() + ymin, xmin, ymax, xmax = tf.unstack(tf.transpose(box_corners)) + width = xmax - xmin + height = ymax - ymin + ycenter = ymin + height / 2. + xcenter = xmin + width / 2. + return [ycenter, xcenter, height, width] + + def transpose_coordinates(self, scope=None): + """Transpose the coordinate representation in a boxlist. + + Args: + scope: name scope of the function. + """ + with tf.name_scope(scope, 'transpose_coordinates'): + y_min, x_min, y_max, x_max = tf.split( + value=self.get(), num_or_size_splits=4, axis=1) + self.set(tf.concat([x_min, y_min, x_max, y_max], 1)) + + def as_tensor_dict(self, fields=None): + """Retrieves specified fields as a dictionary of tensors. + + Args: + fields: (optional) list of fields to return in the dictionary. + If None (default), all fields are returned. + + Returns: + tensor_dict: A dictionary of tensors specified by fields. + + Raises: + ValueError: if specified field is not contained in boxlist. + """ + tensor_dict = {} + if fields is None: + fields = self.get_all_fields() + for field in fields: + if not self.has_field(field): + raise ValueError('boxlist must contain all specified fields') + tensor_dict[field] = self.get_field(field) + return tensor_dict diff --git a/workspace/virtuallab/object_detection/core/box_list.pyc b/workspace/virtuallab/object_detection/core/box_list.pyc new file mode 100644 index 0000000000000000000000000000000000000000..523660681bd0259dbb6ad36a5ae75bdc97fad992 GIT binary patch literal 8181 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 2: + raise ValueError('Scores should have rank 1 or 2') + if len(scores.shape.as_list()) == 2 and scores.shape.as_list()[1] != 1: + raise ValueError('Scores should have rank 1 or have shape ' + 'consistent with [None, 1]') + high_score_indices = tf.cast(tf.reshape( + tf.where(tf.greater(scores, thresh)), + [-1]), tf.int32) + return gather(boxlist, high_score_indices) + + +def non_max_suppression(boxlist, thresh, max_output_size, scope=None): + """Non maximum suppression. + + This op greedily selects a subset of detection bounding boxes, pruning + away boxes that have high IOU (intersection over union) overlap (> thresh) + with already selected boxes. Note that this only works for a single class --- + to apply NMS to multi-class predictions, use MultiClassNonMaxSuppression. + + Args: + boxlist: BoxList holding N boxes. Must contain a 'scores' field + representing detection scores. + thresh: scalar threshold + max_output_size: maximum number of retained boxes + scope: name scope. + + Returns: + a BoxList holding M boxes where M <= max_output_size + Raises: + ValueError: if thresh is not in [0, 1] + """ + with tf.name_scope(scope, 'NonMaxSuppression'): + if not 0 <= thresh <= 1.0: + raise ValueError('thresh must be between 0 and 1') + if not isinstance(boxlist, box_list.BoxList): + raise ValueError('boxlist must be a BoxList') + if not boxlist.has_field('scores'): + raise ValueError('input boxlist must have \'scores\' field') + selected_indices = tf.image.non_max_suppression( + boxlist.get(), boxlist.get_field('scores'), + max_output_size, iou_threshold=thresh) + return gather(boxlist, selected_indices) + + +def _copy_extra_fields(boxlist_to_copy_to, boxlist_to_copy_from): + """Copies the extra fields of boxlist_to_copy_from to boxlist_to_copy_to. + + Args: + boxlist_to_copy_to: BoxList to which extra fields are copied. + boxlist_to_copy_from: BoxList from which fields are copied. + + Returns: + boxlist_to_copy_to with extra fields. + """ + for field in boxlist_to_copy_from.get_extra_fields(): + boxlist_to_copy_to.add_field(field, boxlist_to_copy_from.get_field(field)) + return boxlist_to_copy_to + + +def to_normalized_coordinates(boxlist, height, width, + check_range=True, scope=None): + """Converts absolute box coordinates to normalized coordinates in [0, 1]. + + Usually one uses the dynamic shape of the image or conv-layer tensor: + boxlist = box_list_ops.to_normalized_coordinates(boxlist, + tf.shape(images)[1], + tf.shape(images)[2]), + + This function raises an assertion failed error at graph execution time when + the maximum coordinate is smaller than 1.01 (which means that coordinates are + already normalized). The value 1.01 is to deal with small rounding errors. + + Args: + boxlist: BoxList with coordinates in terms of pixel-locations. + height: Maximum value for height of absolute box coordinates. + width: Maximum value for width of absolute box coordinates. + check_range: If True, checks if the coordinates are normalized or not. + scope: name scope. + + Returns: + boxlist with normalized coordinates in [0, 1]. + """ + with tf.name_scope(scope, 'ToNormalizedCoordinates'): + height = tf.cast(height, tf.float32) + width = tf.cast(width, tf.float32) + + if check_range: + max_val = tf.reduce_max(boxlist.get()) + max_assert = tf.Assert(tf.greater(max_val, 1.01), + ['max value is lower than 1.01: ', max_val]) + with tf.control_dependencies([max_assert]): + width = tf.identity(width) + + return scale(boxlist, 1 / height, 1 / width) + + +def to_absolute_coordinates(boxlist, + height, + width, + check_range=True, + maximum_normalized_coordinate=1.1, + scope=None): + """Converts normalized box coordinates to absolute pixel coordinates. + + This function raises an assertion failed error when the maximum box coordinate + value is larger than maximum_normalized_coordinate (in which case coordinates + are already absolute). + + Args: + boxlist: BoxList with coordinates in range [0, 1]. + height: Maximum value for height of absolute box coordinates. + width: Maximum value for width of absolute box coordinates. + check_range: If True, checks if the coordinates are normalized or not. + maximum_normalized_coordinate: Maximum coordinate value to be considered + as normalized, default to 1.1. + scope: name scope. + + Returns: + boxlist with absolute coordinates in terms of the image size. + + """ + with tf.name_scope(scope, 'ToAbsoluteCoordinates'): + height = tf.cast(height, tf.float32) + width = tf.cast(width, tf.float32) + + # Ensure range of input boxes is correct. + if check_range: + box_maximum = tf.reduce_max(boxlist.get()) + max_assert = tf.Assert( + tf.greater_equal(maximum_normalized_coordinate, box_maximum), + ['maximum box coordinate value is larger ' + 'than %f: ' % maximum_normalized_coordinate, box_maximum]) + with tf.control_dependencies([max_assert]): + width = tf.identity(width) + + return scale(boxlist, height, width) + + +def refine_boxes_multi_class(pool_boxes, + num_classes, + nms_iou_thresh, + nms_max_detections, + voting_iou_thresh=0.5): + """Refines a pool of boxes using non max suppression and box voting. + + Box refinement is done independently for each class. + + Args: + pool_boxes: (BoxList) A collection of boxes to be refined. pool_boxes must + have a rank 1 'scores' field and a rank 1 'classes' field. + num_classes: (int scalar) Number of classes. + nms_iou_thresh: (float scalar) iou threshold for non max suppression (NMS). + nms_max_detections: (int scalar) maximum output size for NMS. + voting_iou_thresh: (float scalar) iou threshold for box voting. + + Returns: + BoxList of refined boxes. + + Raises: + ValueError: if + a) nms_iou_thresh or voting_iou_thresh is not in [0, 1]. + b) pool_boxes is not a BoxList. + c) pool_boxes does not have a scores and classes field. + """ + if not 0.0 <= nms_iou_thresh <= 1.0: + raise ValueError('nms_iou_thresh must be between 0 and 1') + if not 0.0 <= voting_iou_thresh <= 1.0: + raise ValueError('voting_iou_thresh must be between 0 and 1') + if not isinstance(pool_boxes, box_list.BoxList): + raise ValueError('pool_boxes must be a BoxList') + if not pool_boxes.has_field('scores'): + raise ValueError('pool_boxes must have a \'scores\' field') + if not pool_boxes.has_field('classes'): + raise ValueError('pool_boxes must have a \'classes\' field') + + refined_boxes = [] + for i in range(num_classes): + boxes_class = filter_field_value_equals(pool_boxes, 'classes', i) + refined_boxes_class = refine_boxes(boxes_class, nms_iou_thresh, + nms_max_detections, voting_iou_thresh) + refined_boxes.append(refined_boxes_class) + return sort_by_field(concatenate(refined_boxes), 'scores') + + +def refine_boxes(pool_boxes, + nms_iou_thresh, + nms_max_detections, + voting_iou_thresh=0.5): + """Refines a pool of boxes using non max suppression and box voting. + + Args: + pool_boxes: (BoxList) A collection of boxes to be refined. pool_boxes must + have a rank 1 'scores' field. + nms_iou_thresh: (float scalar) iou threshold for non max suppression (NMS). + nms_max_detections: (int scalar) maximum output size for NMS. + voting_iou_thresh: (float scalar) iou threshold for box voting. + + Returns: + BoxList of refined boxes. + + Raises: + ValueError: if + a) nms_iou_thresh or voting_iou_thresh is not in [0, 1]. + b) pool_boxes is not a BoxList. + c) pool_boxes does not have a scores field. + """ + if not 0.0 <= nms_iou_thresh <= 1.0: + raise ValueError('nms_iou_thresh must be between 0 and 1') + if not 0.0 <= voting_iou_thresh <= 1.0: + raise ValueError('voting_iou_thresh must be between 0 and 1') + if not isinstance(pool_boxes, box_list.BoxList): + raise ValueError('pool_boxes must be a BoxList') + if not pool_boxes.has_field('scores'): + raise ValueError('pool_boxes must have a \'scores\' field') + + nms_boxes = non_max_suppression( + pool_boxes, nms_iou_thresh, nms_max_detections) + return box_voting(nms_boxes, pool_boxes, voting_iou_thresh) + + +def box_voting(selected_boxes, pool_boxes, iou_thresh=0.5): + """Performs box voting as described in S. Gidaris and N. Komodakis, ICCV 2015. + + Performs box voting as described in 'Object detection via a multi-region & + semantic segmentation-aware CNN model', Gidaris and Komodakis, ICCV 2015. For + each box 'B' in selected_boxes, we find the set 'S' of boxes in pool_boxes + with iou overlap >= iou_thresh. The location of B is set to the weighted + average location of boxes in S (scores are used for weighting). And the score + of B is set to the average score of boxes in S. + + Args: + selected_boxes: BoxList containing a subset of boxes in pool_boxes. These + boxes are usually selected from pool_boxes using non max suppression. + pool_boxes: BoxList containing a set of (possibly redundant) boxes. + iou_thresh: (float scalar) iou threshold for matching boxes in + selected_boxes and pool_boxes. + + Returns: + BoxList containing averaged locations and scores for each box in + selected_boxes. + + Raises: + ValueError: if + a) selected_boxes or pool_boxes is not a BoxList. + b) if iou_thresh is not in [0, 1]. + c) pool_boxes does not have a scores field. + """ + if not 0.0 <= iou_thresh <= 1.0: + raise ValueError('iou_thresh must be between 0 and 1') + if not isinstance(selected_boxes, box_list.BoxList): + raise ValueError('selected_boxes must be a BoxList') + if not isinstance(pool_boxes, box_list.BoxList): + raise ValueError('pool_boxes must be a BoxList') + if not pool_boxes.has_field('scores'): + raise ValueError('pool_boxes must have a \'scores\' field') + + iou_ = iou(selected_boxes, pool_boxes) + match_indicator = tf.cast(tf.greater(iou_, iou_thresh), dtype=tf.float32) + num_matches = tf.reduce_sum(match_indicator, 1) + # TODO(kbanoop): Handle the case where some boxes in selected_boxes do not + # match to any boxes in pool_boxes. For such boxes without any matches, we + # should return the original boxes without voting. + match_assert = tf.Assert( + tf.reduce_all(tf.greater(num_matches, 0)), + ['Each box in selected_boxes must match with at least one box ' + 'in pool_boxes.']) + + scores = tf.expand_dims(pool_boxes.get_field('scores'), 1) + scores_assert = tf.Assert( + tf.reduce_all(tf.greater_equal(scores, 0)), + ['Scores must be non negative.']) + + with tf.control_dependencies([scores_assert, match_assert]): + sum_scores = tf.matmul(match_indicator, scores) + averaged_scores = tf.reshape(sum_scores, [-1]) / num_matches + + box_locations = tf.matmul(match_indicator, + pool_boxes.get() * scores) / sum_scores + averaged_boxes = box_list.BoxList(box_locations) + _copy_extra_fields(averaged_boxes, selected_boxes) + averaged_boxes.add_field('scores', averaged_scores) + return averaged_boxes + + +def pad_or_clip_box_list(boxlist, num_boxes, scope=None): + """Pads or clips all fields of a BoxList. + + Args: + boxlist: A BoxList with arbitrary of number of boxes. + num_boxes: First num_boxes in boxlist are kept. + The fields are zero-padded if num_boxes is bigger than the + actual number of boxes. + scope: name scope. + + Returns: + BoxList with all fields padded or clipped. + """ + with tf.name_scope(scope, 'PadOrClipBoxList'): + subboxlist = box_list.BoxList(shape_utils.pad_or_clip_tensor( + boxlist.get(), num_boxes)) + for field in boxlist.get_extra_fields(): + subfield = shape_utils.pad_or_clip_tensor( + boxlist.get_field(field), num_boxes) + subboxlist.add_field(field, subfield) + return subboxlist + + +def select_random_box(boxlist, + default_box=None, + seed=None, + scope=None): + """Selects a random bounding box from a `BoxList`. + + Args: + boxlist: A BoxList. + default_box: A [1, 4] float32 tensor. If no boxes are present in `boxlist`, + this default box will be returned. If None, will use a default box of + [[-1., -1., -1., -1.]]. + seed: Random seed. + scope: Name scope. + + Returns: + bbox: A [1, 4] tensor with a random bounding box. + valid: A bool tensor indicating whether a valid bounding box is returned + (True) or whether the default box is returned (False). + """ + with tf.name_scope(scope, 'SelectRandomBox'): + bboxes = boxlist.get() + combined_shape = shape_utils.combined_static_and_dynamic_shape(bboxes) + number_of_boxes = combined_shape[0] + default_box = default_box or tf.constant([[-1., -1., -1., -1.]]) + + def select_box(): + random_index = tf.random_uniform([], + maxval=number_of_boxes, + dtype=tf.int32, + seed=seed) + return tf.expand_dims(bboxes[random_index], axis=0), tf.constant(True) + + return tf.cond( + tf.greater_equal(number_of_boxes, 1), + true_fn=select_box, + false_fn=lambda: (default_box, tf.constant(False))) + + +def get_minimal_coverage_box(boxlist, + default_box=None, + scope=None): + """Creates a single bounding box which covers all boxes in the boxlist. + + Args: + boxlist: A Boxlist. + default_box: A [1, 4] float32 tensor. If no boxes are present in `boxlist`, + this default box will be returned. If None, will use a default box of + [[0., 0., 1., 1.]]. + scope: Name scope. + + Returns: + A [1, 4] float32 tensor with a bounding box that tightly covers all the + boxes in the box list. If the boxlist does not contain any boxes, the + default box is returned. + """ + with tf.name_scope(scope, 'CreateCoverageBox'): + num_boxes = boxlist.num_boxes() + + def coverage_box(bboxes): + y_min, x_min, y_max, x_max = tf.split( + value=bboxes, num_or_size_splits=4, axis=1) + y_min_coverage = tf.reduce_min(y_min, axis=0) + x_min_coverage = tf.reduce_min(x_min, axis=0) + y_max_coverage = tf.reduce_max(y_max, axis=0) + x_max_coverage = tf.reduce_max(x_max, axis=0) + return tf.stack( + [y_min_coverage, x_min_coverage, y_max_coverage, x_max_coverage], + axis=1) + + default_box = default_box or tf.constant([[0., 0., 1., 1.]]) + return tf.cond( + tf.greater_equal(num_boxes, 1), + true_fn=lambda: coverage_box(boxlist.get()), + false_fn=lambda: default_box) + + +def sample_boxes_by_jittering(boxlist, + num_boxes_to_sample, + stddev=0.1, + scope=None): + """Samples num_boxes_to_sample boxes by jittering around boxlist boxes. + + It is possible that this function might generate boxes with size 0. The larger + the stddev, this is more probable. For a small stddev of 0.1 this probability + is very small. + + Args: + boxlist: A boxlist containing N boxes in normalized coordinates. + num_boxes_to_sample: A positive integer containing the number of boxes to + sample. + stddev: Standard deviation. This is used to draw random offsets for the + box corners from a normal distribution. The offset is multiplied by the + box size so will be larger in terms of pixels for larger boxes. + scope: Name scope. + + Returns: + sampled_boxlist: A boxlist containing num_boxes_to_sample boxes in + normalized coordinates. + """ + with tf.name_scope(scope, 'SampleBoxesByJittering'): + num_boxes = boxlist.num_boxes() + box_indices = tf.random_uniform( + [num_boxes_to_sample], + minval=0, + maxval=num_boxes, + dtype=tf.int32) + sampled_boxes = tf.gather(boxlist.get(), box_indices) + sampled_boxes_height = sampled_boxes[:, 2] - sampled_boxes[:, 0] + sampled_boxes_width = sampled_boxes[:, 3] - sampled_boxes[:, 1] + rand_miny_gaussian = tf.random_normal([num_boxes_to_sample], stddev=stddev) + rand_minx_gaussian = tf.random_normal([num_boxes_to_sample], stddev=stddev) + rand_maxy_gaussian = tf.random_normal([num_boxes_to_sample], stddev=stddev) + rand_maxx_gaussian = tf.random_normal([num_boxes_to_sample], stddev=stddev) + miny = rand_miny_gaussian * sampled_boxes_height + sampled_boxes[:, 0] + minx = rand_minx_gaussian * sampled_boxes_width + sampled_boxes[:, 1] + maxy = rand_maxy_gaussian * sampled_boxes_height + sampled_boxes[:, 2] + maxx = rand_maxx_gaussian * sampled_boxes_width + sampled_boxes[:, 3] + maxy = tf.maximum(miny, maxy) + maxx = tf.maximum(minx, maxx) + sampled_boxes = tf.stack([miny, minx, maxy, maxx], axis=1) + sampled_boxes = tf.maximum(tf.minimum(sampled_boxes, 1.0), 0.0) + return box_list.BoxList(sampled_boxes) diff --git a/workspace/virtuallab/object_detection/core/box_list_ops.pyc b/workspace/virtuallab/object_detection/core/box_list_ops.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a759da79ce151fd1ce80eb902938274cd45e9bf5 GIT binary patch literal 46226 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/box_list_ops_test.py b/workspace/virtuallab/object_detection/core/box_list_ops_test.py new file mode 100644 index 0000000..767c189 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/box_list_ops_test.py @@ -0,0 +1,1104 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.box_list_ops.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.utils import test_case + + +class BoxListOpsTest(test_case.TestCase): + """Tests for common bounding box operations.""" + + def test_area(self): + def graph_fn(): + corners = tf.constant([[0.0, 0.0, 10.0, 20.0], [1.0, 2.0, 3.0, 4.0]]) + boxes = box_list.BoxList(corners) + areas = box_list_ops.area(boxes) + return areas + areas_out = self.execute(graph_fn, []) + exp_output = [200.0, 4.0] + self.assertAllClose(areas_out, exp_output) + + def test_height_width(self): + def graph_fn(): + corners = tf.constant([[0.0, 0.0, 10.0, 20.0], [1.0, 2.0, 3.0, 4.0]]) + boxes = box_list.BoxList(corners) + return box_list_ops.height_width(boxes) + heights_out, widths_out = self.execute(graph_fn, []) + exp_output_heights = [10., 2.] + exp_output_widths = [20., 2.] + self.assertAllClose(heights_out, exp_output_heights) + self.assertAllClose(widths_out, exp_output_widths) + + def test_scale(self): + def graph_fn(): + corners = tf.constant([[0, 0, 100, 200], [50, 120, 100, 140]], + dtype=tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.constant([[1], [2]])) + + y_scale = tf.constant(1.0/100) + x_scale = tf.constant(1.0/200) + scaled_boxes = box_list_ops.scale(boxes, y_scale, x_scale) + return scaled_boxes.get(), scaled_boxes.get_field('extra_data') + scaled_corners_out, extra_data_out = self.execute(graph_fn, []) + exp_output = [[0, 0, 1, 1], [0.5, 0.6, 1.0, 0.7]] + self.assertAllClose(scaled_corners_out, exp_output) + self.assertAllEqual(extra_data_out, [[1], [2]]) + + def test_scale_height_width(self): + def graph_fn(): + corners = tf.constant([[-10, -20, 10, 20], [0, 100, 100, 200]], + dtype=tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.constant([[1], [2]])) + + y_scale = tf.constant(2.) + x_scale = tf.constant(0.5) + scaled_boxes = box_list_ops.scale_height_width(boxes, y_scale, x_scale) + return scaled_boxes.get(), scaled_boxes.get_field('extra_data') + exp_output = [ + [-20., -10, 20., 10], + [-50., 125, 150., 175.]] + scaled_corners_out, extra_data_out = self.execute(graph_fn, []) + self.assertAllClose(scaled_corners_out, exp_output) + self.assertAllEqual(extra_data_out, [[1], [2]]) + + def test_clip_to_window_filter_boxes_which_fall_outside_the_window( + self): + def graph_fn(): + window = tf.constant([0, 0, 9, 14], tf.float32) + corners = tf.constant([[5.0, 5.0, 6.0, 6.0], + [-1.0, -2.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], + [0.0, 0.0, 9.0, 14.0], + [-100.0, -100.0, 300.0, 600.0], + [-10.0, -10.0, -9.0, -9.0]]) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.constant([[1], [2], [3], [4], [5], [6]])) + pruned = box_list_ops.clip_to_window( + boxes, window, filter_nonoverlapping=True) + return pruned.get(), pruned.get_field('extra_data') + exp_output = [[5.0, 5.0, 6.0, 6.0], [0.0, 0.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], [0.0, 0.0, 9.0, 14.0], + [0.0, 0.0, 9.0, 14.0]] + pruned_output, extra_data_out = self.execute_cpu(graph_fn, []) + self.assertAllClose(pruned_output, exp_output) + self.assertAllEqual(extra_data_out, [[1], [2], [3], [4], [5]]) + + def test_clip_to_window_without_filtering_boxes_which_fall_outside_the_window( + self): + def graph_fn(): + window = tf.constant([0, 0, 9, 14], tf.float32) + corners = tf.constant([[5.0, 5.0, 6.0, 6.0], + [-1.0, -2.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], + [0.0, 0.0, 9.0, 14.0], + [-100.0, -100.0, 300.0, 600.0], + [-10.0, -10.0, -9.0, -9.0]]) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.constant([[1], [2], [3], [4], [5], [6]])) + pruned = box_list_ops.clip_to_window( + boxes, window, filter_nonoverlapping=False) + return pruned.get(), pruned.get_field('extra_data') + pruned_output, extra_data_out = self.execute(graph_fn, []) + exp_output = [[5.0, 5.0, 6.0, 6.0], [0.0, 0.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], [0.0, 0.0, 9.0, 14.0], + [0.0, 0.0, 9.0, 14.0], [0.0, 0.0, 0.0, 0.0]] + self.assertAllClose(pruned_output, exp_output) + self.assertAllEqual(extra_data_out, [[1], [2], [3], [4], [5], [6]]) + + def test_prune_outside_window_filters_boxes_which_fall_outside_the_window( + self): + def graph_fn(): + window = tf.constant([0, 0, 9, 14], tf.float32) + corners = tf.constant([[5.0, 5.0, 6.0, 6.0], + [-1.0, -2.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], + [0.0, 0.0, 9.0, 14.0], + [-10.0, -10.0, -9.0, -9.0], + [-100.0, -100.0, 300.0, 600.0]]) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.constant([[1], [2], [3], [4], [5], [6]])) + pruned, keep_indices = box_list_ops.prune_outside_window(boxes, window) + return pruned.get(), pruned.get_field('extra_data'), keep_indices + pruned_output, extra_data_out, keep_indices_out = self.execute_cpu(graph_fn, + []) + exp_output = [[5.0, 5.0, 6.0, 6.0], + [2.0, 3.0, 5.0, 9.0], + [0.0, 0.0, 9.0, 14.0]] + self.assertAllClose(pruned_output, exp_output) + self.assertAllEqual(keep_indices_out, [0, 2, 3]) + self.assertAllEqual(extra_data_out, [[1], [3], [4]]) + + def test_prune_completely_outside_window(self): + def graph_fn(): + window = tf.constant([0, 0, 9, 14], tf.float32) + corners = tf.constant([[5.0, 5.0, 6.0, 6.0], + [-1.0, -2.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], + [0.0, 0.0, 9.0, 14.0], + [-10.0, -10.0, -9.0, -9.0], + [-100.0, -100.0, 300.0, 600.0]]) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.constant([[1], [2], [3], [4], [5], [6]])) + pruned, keep_indices = box_list_ops.prune_completely_outside_window( + boxes, window) + return pruned.get(), pruned.get_field('extra_data'), keep_indices + pruned_output, extra_data_out, keep_indices_out = self.execute(graph_fn, []) + exp_output = [[5.0, 5.0, 6.0, 6.0], + [-1.0, -2.0, 4.0, 5.0], + [2.0, 3.0, 5.0, 9.0], + [0.0, 0.0, 9.0, 14.0], + [-100.0, -100.0, 300.0, 600.0]] + self.assertAllClose(pruned_output, exp_output) + self.assertAllEqual(keep_indices_out, [0, 1, 2, 3, 5]) + self.assertAllEqual(extra_data_out, [[1], [2], [3], [4], [6]]) + + def test_prune_completely_outside_window_with_empty_boxlist(self): + def graph_fn(): + window = tf.constant([0, 0, 9, 14], tf.float32) + corners = tf.zeros(shape=[0, 4], dtype=tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('extra_data', tf.zeros(shape=[0], dtype=tf.int32)) + pruned, keep_indices = box_list_ops.prune_completely_outside_window( + boxes, window) + pruned_boxes = pruned.get() + extra = pruned.get_field('extra_data') + return pruned_boxes, extra, keep_indices + + pruned_boxes_out, extra_out, keep_indices_out = self.execute(graph_fn, []) + exp_pruned_boxes = np.zeros(shape=[0, 4], dtype=np.float32) + exp_extra = np.zeros(shape=[0], dtype=np.int32) + self.assertAllClose(exp_pruned_boxes, pruned_boxes_out) + self.assertAllEqual([], keep_indices_out) + self.assertAllEqual(exp_extra, extra_out) + + def test_intersection(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + intersect = box_list_ops.intersection(boxes1, boxes2) + return intersect + exp_output = [[2.0, 0.0, 6.0], [1.0, 0.0, 5.0]] + intersect_out = self.execute(graph_fn, []) + self.assertAllClose(intersect_out, exp_output) + + def test_matched_intersection(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + intersect = box_list_ops.matched_intersection(boxes1, boxes2) + return intersect + exp_output = [2.0, 0.0] + intersect_out = self.execute(graph_fn, []) + self.assertAllClose(intersect_out, exp_output) + + def test_iou(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + iou = box_list_ops.iou(boxes1, boxes2) + return iou + exp_output = [[2.0 / 16.0, 0, 6.0 / 400.0], [1.0 / 16.0, 0.0, 5.0 / 400.0]] + iou_output = self.execute(graph_fn, []) + self.assertAllClose(iou_output, exp_output) + + def test_l1(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + l1 = box_list_ops.l1(boxes1, boxes2) + return l1 + exp_output = [[5.0, 22.5, 45.5], [8.5, 19.0, 40.0]] + l1_output = self.execute(graph_fn, []) + self.assertAllClose(l1_output, exp_output) + + def test_giou(self): + def graph_fn(): + corners1 = tf.constant([[5.0, 7.0, 7.0, 9.0]]) + corners2 = tf.constant([[5.0, 7.0, 7.0, 9.0], [5.0, 11.0, 7.0, 13.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + giou = box_list_ops.giou(boxes1, boxes2) + return giou + exp_output = [[1.0, -1.0 / 3.0]] + giou_output = self.execute(graph_fn, []) + self.assertAllClose(giou_output, exp_output) + + def test_matched_iou(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + iou = box_list_ops.matched_iou(boxes1, boxes2) + return iou + exp_output = [2.0 / 16.0, 0] + iou_output = self.execute(graph_fn, []) + self.assertAllClose(iou_output, exp_output) + + def test_iouworks_on_empty_inputs(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + boxes_empty = box_list.BoxList(tf.zeros((0, 4))) + iou_empty_1 = box_list_ops.iou(boxes1, boxes_empty) + iou_empty_2 = box_list_ops.iou(boxes_empty, boxes2) + iou_empty_3 = box_list_ops.iou(boxes_empty, boxes_empty) + return iou_empty_1, iou_empty_2, iou_empty_3 + iou_output_1, iou_output_2, iou_output_3 = self.execute(graph_fn, []) + self.assertAllEqual(iou_output_1.shape, (2, 0)) + self.assertAllEqual(iou_output_2.shape, (0, 3)) + self.assertAllEqual(iou_output_3.shape, (0, 0)) + + def test_ioa(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + ioa_1 = box_list_ops.ioa(boxes1, boxes2) + ioa_2 = box_list_ops.ioa(boxes2, boxes1) + return ioa_1, ioa_2 + exp_output_1 = [[2.0 / 12.0, 0, 6.0 / 400.0], + [1.0 / 12.0, 0.0, 5.0 / 400.0]] + exp_output_2 = [[2.0 / 6.0, 1.0 / 5.0], + [0, 0], + [6.0 / 6.0, 5.0 / 5.0]] + ioa_output_1, ioa_output_2 = self.execute(graph_fn, []) + self.assertAllClose(ioa_output_1, exp_output_1) + self.assertAllClose(ioa_output_2, exp_output_2) + + def test_prune_non_overlapping_boxes(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + minoverlap = 0.5 + + exp_output_1 = boxes1 + exp_output_2 = box_list.BoxList(tf.constant(0.0, shape=[0, 4])) + output_1, keep_indices_1 = box_list_ops.prune_non_overlapping_boxes( + boxes1, boxes2, min_overlap=minoverlap) + output_2, keep_indices_2 = box_list_ops.prune_non_overlapping_boxes( + boxes2, boxes1, min_overlap=minoverlap) + return (output_1.get(), keep_indices_1, output_2.get(), keep_indices_2, + exp_output_1.get(), exp_output_2.get()) + + (output_1_, keep_indices_1_, output_2_, keep_indices_2_, exp_output_1_, + exp_output_2_) = self.execute_cpu(graph_fn, []) + self.assertAllClose(output_1_, exp_output_1_) + self.assertAllClose(output_2_, exp_output_2_) + self.assertAllEqual(keep_indices_1_, [0, 1]) + self.assertAllEqual(keep_indices_2_, []) + + def test_prune_small_boxes(self): + def graph_fn(): + boxes = tf.constant([[4.0, 3.0, 7.0, 5.0], + [5.0, 6.0, 10.0, 7.0], + [3.0, 4.0, 6.0, 8.0], + [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes = box_list.BoxList(boxes) + pruned_boxes = box_list_ops.prune_small_boxes(boxes, 3) + return pruned_boxes.get() + exp_boxes = [[3.0, 4.0, 6.0, 8.0], + [0.0, 0.0, 20.0, 20.0]] + pruned_boxes = self.execute(graph_fn, []) + self.assertAllEqual(pruned_boxes, exp_boxes) + + def test_prune_small_boxes_prunes_boxes_with_negative_side(self): + def graph_fn(): + boxes = tf.constant([[4.0, 3.0, 7.0, 5.0], + [5.0, 6.0, 10.0, 7.0], + [3.0, 4.0, 6.0, 8.0], + [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0], + [2.0, 3.0, 1.5, 7.0], # negative height + [2.0, 3.0, 5.0, 1.7]]) # negative width + boxes = box_list.BoxList(boxes) + pruned_boxes = box_list_ops.prune_small_boxes(boxes, 3) + return pruned_boxes.get() + exp_boxes = [[3.0, 4.0, 6.0, 8.0], + [0.0, 0.0, 20.0, 20.0]] + pruned_boxes = self.execute_cpu(graph_fn, []) + self.assertAllEqual(pruned_boxes, exp_boxes) + + def test_change_coordinate_frame(self): + def graph_fn(): + corners = tf.constant([[0.25, 0.5, 0.75, 0.75], [0.5, 0.0, 1.0, 1.0]]) + window = tf.constant([0.25, 0.25, 0.75, 0.75]) + boxes = box_list.BoxList(corners) + + expected_corners = tf.constant([[0, 0.5, 1.0, 1.0], + [0.5, -0.5, 1.5, 1.5]]) + expected_boxes = box_list.BoxList(expected_corners) + output = box_list_ops.change_coordinate_frame(boxes, window) + return output.get(), expected_boxes.get() + output_, expected_boxes_ = self.execute(graph_fn, []) + self.assertAllClose(output_, expected_boxes_) + + def test_ioaworks_on_empty_inputs(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + boxes_empty = box_list.BoxList(tf.zeros((0, 4))) + ioa_empty_1 = box_list_ops.ioa(boxes1, boxes_empty) + ioa_empty_2 = box_list_ops.ioa(boxes_empty, boxes2) + ioa_empty_3 = box_list_ops.ioa(boxes_empty, boxes_empty) + return ioa_empty_1, ioa_empty_2, ioa_empty_3 + ioa_output_1, ioa_output_2, ioa_output_3 = self.execute(graph_fn, []) + self.assertAllEqual(ioa_output_1.shape, (2, 0)) + self.assertAllEqual(ioa_output_2.shape, (0, 3)) + self.assertAllEqual(ioa_output_3.shape, (0, 0)) + + def test_pairwise_distances(self): + def graph_fn(): + corners1 = tf.constant([[0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 0.0, 2.0]]) + corners2 = tf.constant([[3.0, 4.0, 1.0, 0.0], + [-4.0, 0.0, 0.0, 3.0], + [0.0, 0.0, 0.0, 0.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + dist_matrix = box_list_ops.sq_dist(boxes1, boxes2) + return dist_matrix + exp_output = [[26, 25, 0], [18, 27, 6]] + dist_output = self.execute(graph_fn, []) + self.assertAllClose(dist_output, exp_output) + + def test_boolean_mask(self): + def graph_fn(): + corners = tf.constant( + [4 * [0.0], 4 * [1.0], 4 * [2.0], 4 * [3.0], 4 * [4.0]]) + indicator = tf.constant([True, False, True, False, True], tf.bool) + boxes = box_list.BoxList(corners) + subset = box_list_ops.boolean_mask(boxes, indicator) + return subset.get() + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + subset_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(subset_output, expected_subset) + + def test_static_boolean_mask_with_field(self): + + def graph_fn(corners, weights, indicator): + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + subset = box_list_ops.boolean_mask( + boxes, + indicator, ['weights'], + use_static_shapes=True, + indicator_sum=3) + return (subset.get_field('boxes'), subset.get_field('weights')) + + corners = np.array( + [4 * [0.0], 4 * [1.0], 4 * [2.0], 4 * [3.0], 4 * [4.0]], + dtype=np.float32) + indicator = np.array([True, False, True, False, True], dtype=np.bool) + weights = np.array([[.1], [.3], [.5], [.7], [.9]], dtype=np.float32) + result_boxes, result_weights = self.execute_cpu( + graph_fn, [corners, weights, indicator]) + expected_boxes = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + expected_weights = [[.1], [.5], [.9]] + + self.assertAllClose(result_boxes, expected_boxes) + self.assertAllClose(result_weights, expected_weights) + + def test_gather(self): + def graph_fn(): + corners = tf.constant( + [4 * [0.0], 4 * [1.0], 4 * [2.0], 4 * [3.0], 4 * [4.0]]) + indices = tf.constant([0, 2, 4], tf.int32) + boxes = box_list.BoxList(corners) + subset = box_list_ops.gather(boxes, indices) + return subset.get() + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + subset_output = self.execute(graph_fn, []) + self.assertAllClose(subset_output, expected_subset) + + def test_static_gather_with_field(self): + + def graph_fn(corners, weights, indices): + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + subset = box_list_ops.gather( + boxes, indices, ['weights'], use_static_shapes=True) + return (subset.get_field('boxes'), subset.get_field('weights')) + + corners = np.array([4 * [0.0], 4 * [1.0], 4 * [2.0], 4 * [3.0], + 4 * [4.0]], dtype=np.float32) + weights = np.array([[.1], [.3], [.5], [.7], [.9]], dtype=np.float32) + indices = np.array([0, 2, 4], dtype=np.int32) + + result_boxes, result_weights = self.execute(graph_fn, + [corners, weights, indices]) + expected_boxes = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + expected_weights = [[.1], [.5], [.9]] + self.assertAllClose(result_boxes, expected_boxes) + self.assertAllClose(result_weights, expected_weights) + + def test_gather_with_invalid_field(self): + corners = tf.constant([4 * [0.0], 4 * [1.0]]) + indices = tf.constant([0, 1], tf.int32) + weights = tf.constant([[.1], [.3]], tf.float32) + + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + with self.assertRaises(ValueError): + box_list_ops.gather(boxes, indices, ['foo', 'bar']) + + def test_gather_with_invalid_inputs(self): + corners = tf.constant( + [4 * [0.0], 4 * [1.0], 4 * [2.0], 4 * [3.0], 4 * [4.0]]) + indices_float32 = tf.constant([0, 2, 4], tf.float32) + boxes = box_list.BoxList(corners) + with self.assertRaises(ValueError): + _ = box_list_ops.gather(boxes, indices_float32) + indices_2d = tf.constant([[0, 2, 4]], tf.int32) + boxes = box_list.BoxList(corners) + with self.assertRaises(ValueError): + _ = box_list_ops.gather(boxes, indices_2d) + + def test_gather_with_dynamic_indexing(self): + def graph_fn(): + corners = tf.constant( + [4 * [0.0], 4 * [1.0], 4 * [2.0], 4 * [3.0], 4 * [4.0]]) + weights = tf.constant([.5, .3, .7, .1, .9], tf.float32) + indices = tf.reshape(tf.where(tf.greater(weights, 0.4)), [-1]) + boxes = box_list.BoxList(corners) + boxes.add_field('weights', weights) + subset = box_list_ops.gather(boxes, indices, ['weights']) + return subset.get(), subset.get_field('weights') + expected_subset = [4 * [0.0], 4 * [2.0], 4 * [4.0]] + expected_weights = [.5, .7, .9] + subset_output, weights_output = self.execute(graph_fn, []) + self.assertAllClose(subset_output, expected_subset) + self.assertAllClose(weights_output, expected_weights) + + def test_sort_by_field_ascending_order(self): + exp_corners = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], + [0, 10, 1, 11], [0, 10.1, 1, 11.1], [0, 100, 1, 101]] + exp_scores = [.95, .9, .75, .6, .5, .3] + exp_weights = [.2, .45, .6, .75, .8, .92] + + def graph_fn(): + shuffle = [2, 4, 0, 5, 1, 3] + corners = tf.constant([exp_corners[i] for i in shuffle], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant( + [exp_scores[i] for i in shuffle], tf.float32)) + boxes.add_field('weights', tf.constant( + [exp_weights[i] for i in shuffle], tf.float32)) + sort_by_weight = box_list_ops.sort_by_field( + boxes, + 'weights', + order=box_list_ops.SortOrder.ascend) + return [sort_by_weight.get(), sort_by_weight.get_field('scores'), + sort_by_weight.get_field('weights')] + corners_out, scores_out, weights_out = self.execute(graph_fn, []) + self.assertAllClose(corners_out, exp_corners) + self.assertAllClose(scores_out, exp_scores) + self.assertAllClose(weights_out, exp_weights) + + def test_sort_by_field_descending_order(self): + exp_corners = [[0, 0, 1, 1], [0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], + [0, 10, 1, 11], [0, 10.1, 1, 11.1], [0, 100, 1, 101]] + exp_scores = [.95, .9, .75, .6, .5, .3] + exp_weights = [.2, .45, .6, .75, .8, .92] + + def graph_fn(): + shuffle = [2, 4, 0, 5, 1, 3] + corners = tf.constant([exp_corners[i] for i in shuffle], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant( + [exp_scores[i] for i in shuffle], tf.float32)) + boxes.add_field('weights', tf.constant( + [exp_weights[i] for i in shuffle], tf.float32)) + sort_by_score = box_list_ops.sort_by_field(boxes, 'scores') + return (sort_by_score.get(), sort_by_score.get_field('scores'), + sort_by_score.get_field('weights')) + + corners_out, scores_out, weights_out = self.execute(graph_fn, []) + self.assertAllClose(corners_out, exp_corners) + self.assertAllClose(scores_out, exp_scores) + self.assertAllClose(weights_out, exp_weights) + + def test_sort_by_field_invalid_inputs(self): + corners = tf.constant([4 * [0.0], 4 * [0.5], 4 * [1.0], 4 * [2.0], 4 * + [3.0], 4 * [4.0]]) + misc = tf.constant([[.95, .9], [.5, .3]], tf.float32) + weights = tf.constant([[.1, .2]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('misc', misc) + boxes.add_field('weights', weights) + + with self.assertRaises(ValueError): + box_list_ops.sort_by_field(boxes, 'area') + + with self.assertRaises(ValueError): + box_list_ops.sort_by_field(boxes, 'misc') + + with self.assertRaises(ValueError): + box_list_ops.sort_by_field(boxes, 'weights') + + def test_visualize_boxes_in_image(self): + def graph_fn(): + image = tf.zeros((6, 4, 3)) + corners = tf.constant([[0, 0, 5, 3], + [0, 0, 3, 2]], tf.float32) + boxes = box_list.BoxList(corners) + image_and_boxes = box_list_ops.visualize_boxes_in_image(image, boxes) + image_and_boxes_bw = tf.cast( + tf.greater(tf.reduce_sum(image_and_boxes, 2), 0.0), dtype=tf.float32) + return image_and_boxes_bw + exp_result = [[1, 1, 1, 0], + [1, 1, 1, 0], + [1, 1, 1, 0], + [1, 0, 1, 0], + [1, 1, 1, 0], + [0, 0, 0, 0]] + output = self.execute_cpu(graph_fn, []) + self.assertAllEqual(output.astype(int), exp_result) + + def test_filter_field_value_equals(self): + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1], + [0, -0.1, 1, 0.9], + [0, 10, 1, 11], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('classes', tf.constant([1, 2, 1, 2, 2, 1])) + filtered_boxes1 = box_list_ops.filter_field_value_equals( + boxes, 'classes', 1) + filtered_boxes2 = box_list_ops.filter_field_value_equals( + boxes, 'classes', 2) + return filtered_boxes1.get(), filtered_boxes2.get() + exp_output1 = [[0, 0, 1, 1], [0, -0.1, 1, 0.9], [0, 100, 1, 101]] + exp_output2 = [[0, 0.1, 1, 1.1], [0, 10, 1, 11], [0, 10.1, 1, 11.1]] + filtered_output1, filtered_output2 = self.execute_cpu(graph_fn, []) + self.assertAllClose(filtered_output1, exp_output1) + self.assertAllClose(filtered_output2, exp_output2) + + def test_filter_greater_than(self): + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1], + [0, -0.1, 1, 0.9], + [0, 10, 1, 11], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant([.1, .75, .9, .5, .5, .8])) + thresh = .6 + filtered_boxes = box_list_ops.filter_greater_than(boxes, thresh) + return filtered_boxes.get() + exp_output = [[0, 0.1, 1, 1.1], [0, -0.1, 1, 0.9], [0, 100, 1, 101]] + filtered_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(filtered_output, exp_output) + + def test_clip_box_list(self): + def graph_fn(): + boxlist = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5], + [0.6, 0.6, 0.8, 0.8], [0.2, 0.2, 0.3, 0.3]], tf.float32)) + boxlist.add_field('classes', tf.constant([0, 0, 1, 1])) + boxlist.add_field('scores', tf.constant([0.75, 0.65, 0.3, 0.2])) + num_boxes = 2 + clipped_boxlist = box_list_ops.pad_or_clip_box_list(boxlist, num_boxes) + return (clipped_boxlist.get(), clipped_boxlist.get_field('classes'), + clipped_boxlist.get_field('scores')) + + expected_boxes = [[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]] + expected_classes = [0, 0] + expected_scores = [0.75, 0.65] + boxes_out, classes_out, scores_out = self.execute(graph_fn, []) + + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllEqual(expected_classes, classes_out) + self.assertAllClose(expected_scores, scores_out) + + def test_pad_box_list(self): + def graph_fn(): + boxlist = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]], tf.float32)) + boxlist.add_field('classes', tf.constant([0, 1])) + boxlist.add_field('scores', tf.constant([0.75, 0.2])) + num_boxes = 4 + padded_boxlist = box_list_ops.pad_or_clip_box_list(boxlist, num_boxes) + return (padded_boxlist.get(), padded_boxlist.get_field('classes'), + padded_boxlist.get_field('scores')) + expected_boxes = [[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5], + [0, 0, 0, 0], [0, 0, 0, 0]] + expected_classes = [0, 1, 0, 0] + expected_scores = [0.75, 0.2, 0, 0] + boxes_out, classes_out, scores_out = self.execute(graph_fn, []) + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllEqual(expected_classes, classes_out) + self.assertAllClose(expected_scores, scores_out) + + def test_select_random_box(self): + boxes = [[0., 0., 1., 1.], + [0., 1., 2., 3.], + [0., 2., 3., 4.]] + def graph_fn(): + corners = tf.constant(boxes, dtype=tf.float32) + boxlist = box_list.BoxList(corners) + random_bbox, valid = box_list_ops.select_random_box(boxlist) + return random_bbox, valid + random_bbox_out, valid_out = self.execute(graph_fn, []) + norm_small = any( + [np.linalg.norm(random_bbox_out - box) < 1e-6 for box in boxes]) + self.assertTrue(norm_small) + self.assertTrue(valid_out) + + def test_select_random_box_with_empty_boxlist(self): + def graph_fn(): + corners = tf.constant([], shape=[0, 4], dtype=tf.float32) + boxlist = box_list.BoxList(corners) + random_bbox, valid = box_list_ops.select_random_box(boxlist) + return random_bbox, valid + random_bbox_out, valid_out = self.execute_cpu(graph_fn, []) + expected_bbox_out = np.array([[-1., -1., -1., -1.]], dtype=np.float32) + self.assertAllEqual(expected_bbox_out, random_bbox_out) + self.assertFalse(valid_out) + + def test_get_minimal_coverage_box(self): + def graph_fn(): + boxes = [[0., 0., 1., 1.], + [-1., 1., 2., 3.], + [0., 2., 3., 4.]] + corners = tf.constant(boxes, dtype=tf.float32) + boxlist = box_list.BoxList(corners) + coverage_box = box_list_ops.get_minimal_coverage_box(boxlist) + return coverage_box + coverage_box_out = self.execute(graph_fn, []) + expected_coverage_box = [[-1., 0., 3., 4.]] + self.assertAllClose(expected_coverage_box, coverage_box_out) + + def test_get_minimal_coverage_box_with_empty_boxlist(self): + def graph_fn(): + corners = tf.constant([], shape=[0, 4], dtype=tf.float32) + boxlist = box_list.BoxList(corners) + coverage_box = box_list_ops.get_minimal_coverage_box(boxlist) + return coverage_box + coverage_box_out = self.execute(graph_fn, []) + self.assertAllClose([[0.0, 0.0, 1.0, 1.0]], coverage_box_out) + + +class ConcatenateTest(test_case.TestCase): + + def test_invalid_input_box_list_list(self): + with self.assertRaises(ValueError): + box_list_ops.concatenate(None) + with self.assertRaises(ValueError): + box_list_ops.concatenate([]) + with self.assertRaises(ValueError): + corners = tf.constant([[0, 0, 0, 0]], tf.float32) + boxlist = box_list.BoxList(corners) + box_list_ops.concatenate([boxlist, 2]) + + def test_concatenate_with_missing_fields(self): + corners1 = tf.constant([[0, 0, 0, 0], [1, 2, 3, 4]], tf.float32) + scores1 = tf.constant([1.0, 2.1]) + corners2 = tf.constant([[0, 3, 1, 6], [2, 4, 3, 8]], tf.float32) + boxlist1 = box_list.BoxList(corners1) + boxlist1.add_field('scores', scores1) + boxlist2 = box_list.BoxList(corners2) + with self.assertRaises(ValueError): + box_list_ops.concatenate([boxlist1, boxlist2]) + + def test_concatenate_with_incompatible_field_shapes(self): + corners1 = tf.constant([[0, 0, 0, 0], [1, 2, 3, 4]], tf.float32) + scores1 = tf.constant([1.0, 2.1]) + corners2 = tf.constant([[0, 3, 1, 6], [2, 4, 3, 8]], tf.float32) + scores2 = tf.constant([[1.0, 1.0], [2.1, 3.2]]) + boxlist1 = box_list.BoxList(corners1) + boxlist1.add_field('scores', scores1) + boxlist2 = box_list.BoxList(corners2) + boxlist2.add_field('scores', scores2) + with self.assertRaises(ValueError): + box_list_ops.concatenate([boxlist1, boxlist2]) + + def test_concatenate_is_correct(self): + def graph_fn(): + corners1 = tf.constant([[0, 0, 0, 0], [1, 2, 3, 4]], tf.float32) + scores1 = tf.constant([1.0, 2.1]) + corners2 = tf.constant([[0, 3, 1, 6], [2, 4, 3, 8], [1, 0, 5, 10]], + tf.float32) + scores2 = tf.constant([1.0, 2.1, 5.6]) + boxlist1 = box_list.BoxList(corners1) + boxlist1.add_field('scores', scores1) + boxlist2 = box_list.BoxList(corners2) + boxlist2.add_field('scores', scores2) + result = box_list_ops.concatenate([boxlist1, boxlist2]) + return result.get(), result.get_field('scores') + exp_corners = [[0, 0, 0, 0], + [1, 2, 3, 4], + [0, 3, 1, 6], + [2, 4, 3, 8], + [1, 0, 5, 10]] + exp_scores = [1.0, 2.1, 1.0, 2.1, 5.6] + corners_output, scores_output = self.execute(graph_fn, []) + self.assertAllClose(corners_output, exp_corners) + self.assertAllClose(scores_output, exp_scores) + + +class NonMaxSuppressionTest(test_case.TestCase): + + def test_select_from_three_clusters(self): + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1], + [0, -0.1, 1, 0.9], + [0, 10, 1, 11], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant([.9, .75, .6, .95, .5, .3])) + iou_thresh = .5 + max_output_size = 3 + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + return nms.get() + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 100, 1, 101]] + nms_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(nms_output, exp_nms) + + def test_select_at_most_two_boxes_from_three_clusters(self): + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1], + [0, -0.1, 1, 0.9], + [0, 10, 1, 11], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant([.9, .75, .6, .95, .5, .3])) + iou_thresh = .5 + max_output_size = 2 + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + return nms.get() + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1]] + nms_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(nms_output, exp_nms) + + def test_select_at_most_thirty_boxes_from_three_clusters(self): + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1], + [0, -0.1, 1, 0.9], + [0, 10, 1, 11], + [0, 10.1, 1, 11.1], + [0, 100, 1, 101]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant([.9, .75, .6, .95, .5, .3])) + iou_thresh = .5 + max_output_size = 30 + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + return nms.get() + exp_nms = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 100, 1, 101]] + nms_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(nms_output, exp_nms) + + def test_select_single_box(self): + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant([.9])) + iou_thresh = .5 + max_output_size = 3 + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + return nms.get() + exp_nms = [[0, 0, 1, 1]] + nms_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(nms_output, exp_nms) + + def test_select_from_ten_identical_boxes(self): + def graph_fn(): + corners = tf.constant(10 * [[0, 0, 1, 1]], tf.float32) + boxes = box_list.BoxList(corners) + boxes.add_field('scores', tf.constant(10 * [.9])) + iou_thresh = .5 + max_output_size = 3 + nms = box_list_ops.non_max_suppression( + boxes, iou_thresh, max_output_size) + return nms.get() + exp_nms = [[0, 0, 1, 1]] + nms_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(nms_output, exp_nms) + + def test_copy_extra_fields(self): + tensor1 = np.array([[1], [4]]) + tensor2 = np.array([[1, 1], [2, 2]]) + def graph_fn(): + corners = tf.constant([[0, 0, 1, 1], + [0, 0.1, 1, 1.1]], tf.float32) + boxes = box_list.BoxList(corners) + + boxes.add_field('tensor1', tf.constant(tensor1)) + boxes.add_field('tensor2', tf.constant(tensor2)) + new_boxes = box_list.BoxList(tf.constant([[0, 0, 10, 10], + [1, 3, 5, 5]], tf.float32)) + new_boxes = box_list_ops._copy_extra_fields(new_boxes, boxes) + return new_boxes.get_field('tensor1'), new_boxes.get_field('tensor2') + tensor1_out, tensor2_out = self.execute_cpu(graph_fn, []) + self.assertAllClose(tensor1, tensor1_out) + self.assertAllClose(tensor2, tensor2_out) + + +class CoordinatesConversionTest(test_case.TestCase): + + def test_to_normalized_coordinates(self): + def graph_fn(): + coordinates = tf.constant([[0, 0, 100, 100], + [25, 25, 75, 75]], tf.float32) + img = tf.ones((128, 100, 100, 3)) + boxlist = box_list.BoxList(coordinates) + normalized_boxlist = box_list_ops.to_normalized_coordinates( + boxlist, tf.shape(img)[1], tf.shape(img)[2]) + return normalized_boxlist.get() + expected_boxes = [[0, 0, 1, 1], + [0.25, 0.25, 0.75, 0.75]] + normalized_boxes = self.execute(graph_fn, []) + self.assertAllClose(normalized_boxes, expected_boxes) + + def test_to_normalized_coordinates_already_normalized(self): + def graph_fn(): + coordinates = tf.constant([[0, 0, 1, 1], + [0.25, 0.25, 0.75, 0.75]], tf.float32) + img = tf.ones((128, 100, 100, 3)) + boxlist = box_list.BoxList(coordinates) + normalized_boxlist = box_list_ops.to_normalized_coordinates( + boxlist, tf.shape(img)[1], tf.shape(img)[2]) + return normalized_boxlist.get() + with self.assertRaisesOpError('assertion failed'): + self.execute_cpu(graph_fn, []) + + def test_to_absolute_coordinates(self): + def graph_fn(): + coordinates = tf.constant([[0, 0, 1, 1], + [0.25, 0.25, 0.75, 0.75]], tf.float32) + img = tf.ones((128, 100, 100, 3)) + boxlist = box_list.BoxList(coordinates) + absolute_boxlist = box_list_ops.to_absolute_coordinates(boxlist, + tf.shape(img)[1], + tf.shape(img)[2]) + return absolute_boxlist.get() + expected_boxes = [[0, 0, 100, 100], + [25, 25, 75, 75]] + absolute_boxes = self.execute(graph_fn, []) + self.assertAllClose(absolute_boxes, expected_boxes) + + def test_to_absolute_coordinates_already_abolute(self): + def graph_fn(): + coordinates = tf.constant([[0, 0, 100, 100], + [25, 25, 75, 75]], tf.float32) + img = tf.ones((128, 100, 100, 3)) + boxlist = box_list.BoxList(coordinates) + absolute_boxlist = box_list_ops.to_absolute_coordinates(boxlist, + tf.shape(img)[1], + tf.shape(img)[2]) + return absolute_boxlist.get() + with self.assertRaisesOpError('assertion failed'): + self.execute_cpu(graph_fn, []) + + def test_convert_to_normalized_and_back(self): + coordinates = np.random.uniform(size=(100, 4)) + coordinates = np.round(np.sort(coordinates) * 200) + coordinates[:, 2:4] += 1 + coordinates[99, :] = [0, 0, 201, 201] + def graph_fn(): + img = tf.ones((128, 202, 202, 3)) + + boxlist = box_list.BoxList(tf.constant(coordinates, tf.float32)) + boxlist = box_list_ops.to_normalized_coordinates(boxlist, + tf.shape(img)[1], + tf.shape(img)[2]) + boxlist = box_list_ops.to_absolute_coordinates(boxlist, + tf.shape(img)[1], + tf.shape(img)[2]) + return boxlist.get() + out = self.execute(graph_fn, []) + self.assertAllClose(out, coordinates) + + def test_convert_to_absolute_and_back(self): + coordinates = np.random.uniform(size=(100, 4)) + coordinates = np.sort(coordinates) + coordinates[99, :] = [0, 0, 1, 1] + def graph_fn(): + img = tf.ones((128, 202, 202, 3)) + boxlist = box_list.BoxList(tf.constant(coordinates, tf.float32)) + boxlist = box_list_ops.to_absolute_coordinates(boxlist, + tf.shape(img)[1], + tf.shape(img)[2]) + boxlist = box_list_ops.to_normalized_coordinates(boxlist, + tf.shape(img)[1], + tf.shape(img)[2]) + return boxlist.get() + out = self.execute(graph_fn, []) + self.assertAllClose(out, coordinates) + + def test_to_absolute_coordinates_maximum_coordinate_check(self): + def graph_fn(): + coordinates = tf.constant([[0, 0, 1.2, 1.2], + [0.25, 0.25, 0.75, 0.75]], tf.float32) + img = tf.ones((128, 100, 100, 3)) + boxlist = box_list.BoxList(coordinates) + absolute_boxlist = box_list_ops.to_absolute_coordinates( + boxlist, + tf.shape(img)[1], + tf.shape(img)[2], + maximum_normalized_coordinate=1.1) + return absolute_boxlist.get() + with self.assertRaisesOpError('assertion failed'): + self.execute_cpu(graph_fn, []) + + +class BoxRefinementTest(test_case.TestCase): + + def test_box_voting(self): + def graph_fn(): + candidates = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.6, 0.6, 0.8, 0.8]], tf.float32)) + candidates.add_field('ExtraField', tf.constant([1, 2])) + pool = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5], + [0.6, 0.6, 0.8, 0.8]], tf.float32)) + pool.add_field('scores', tf.constant([0.75, 0.25, 0.3])) + averaged_boxes = box_list_ops.box_voting(candidates, pool) + return (averaged_boxes.get(), averaged_boxes.get_field('scores'), + averaged_boxes.get_field('ExtraField')) + + expected_boxes = [[0.1, 0.1, 0.425, 0.425], [0.6, 0.6, 0.8, 0.8]] + expected_scores = [0.5, 0.3] + boxes_out, scores_out, extra_field_out = self.execute(graph_fn, []) + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllClose(expected_scores, scores_out) + self.assertAllEqual(extra_field_out, [1, 2]) + + def test_box_voting_fails_with_negative_scores(self): + def graph_fn(): + candidates = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4]], tf.float32)) + pool = box_list.BoxList(tf.constant([[0.1, 0.1, 0.4, 0.4]], tf.float32)) + pool.add_field('scores', tf.constant([-0.2])) + averaged_boxes = box_list_ops.box_voting(candidates, pool) + return averaged_boxes.get() + + with self.assertRaisesOpError('Scores must be non negative'): + self.execute_cpu(graph_fn, []) + + def test_box_voting_fails_when_unmatched(self): + def graph_fn(): + candidates = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4]], tf.float32)) + pool = box_list.BoxList(tf.constant([[0.6, 0.6, 0.8, 0.8]], tf.float32)) + pool.add_field('scores', tf.constant([0.2])) + averaged_boxes = box_list_ops.box_voting(candidates, pool) + return averaged_boxes.get() + with self.assertRaisesOpError('Each box in selected_boxes must match ' + 'with at least one box in pool_boxes.'): + self.execute_cpu(graph_fn, []) + + def test_refine_boxes(self): + def graph_fn(): + pool = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5], + [0.6, 0.6, 0.8, 0.8]], tf.float32)) + pool.add_field('ExtraField', tf.constant([1, 2, 3])) + pool.add_field('scores', tf.constant([0.75, 0.25, 0.3])) + averaged_boxes = box_list_ops.refine_boxes(pool, 0.5, 10) + return (averaged_boxes.get(), averaged_boxes.get_field('scores'), + averaged_boxes.get_field('ExtraField')) + boxes_out, scores_out, extra_field_out = self.execute_cpu(graph_fn, []) + expected_boxes = [[0.1, 0.1, 0.425, 0.425], [0.6, 0.6, 0.8, 0.8]] + expected_scores = [0.5, 0.3] + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllClose(expected_scores, scores_out) + self.assertAllEqual(extra_field_out, [1, 3]) + + def test_refine_boxes_multi_class(self): + def graph_fn(): + pool = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5], + [0.6, 0.6, 0.8, 0.8], [0.2, 0.2, 0.3, 0.3]], tf.float32)) + pool.add_field('classes', tf.constant([0, 0, 1, 1])) + pool.add_field('scores', tf.constant([0.75, 0.25, 0.3, 0.2])) + averaged_boxes = box_list_ops.refine_boxes_multi_class(pool, 3, 0.5, 10) + return (averaged_boxes.get(), averaged_boxes.get_field('scores'), + averaged_boxes.get_field('classes')) + boxes_out, scores_out, extra_field_out = self.execute_cpu(graph_fn, []) + expected_boxes = [[0.1, 0.1, 0.425, 0.425], [0.6, 0.6, 0.8, 0.8], + [0.2, 0.2, 0.3, 0.3]] + expected_scores = [0.5, 0.3, 0.2] + self.assertAllClose(expected_boxes, boxes_out) + self.assertAllClose(expected_scores, scores_out) + self.assertAllEqual(extra_field_out, [0, 1, 1]) + + def test_sample_boxes_by_jittering(self): + def graph_fn(): + boxes = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], + [0.1, 0.1, 0.5, 0.5], + [0.6, 0.6, 0.8, 0.8], + [0.2, 0.2, 0.3, 0.3]], tf.float32)) + sampled_boxes = box_list_ops.sample_boxes_by_jittering( + boxlist=boxes, num_boxes_to_sample=10) + iou = box_list_ops.iou(boxes, sampled_boxes) + iou_max = tf.reduce_max(iou, axis=0) + return sampled_boxes.get(), iou_max + np_sampled_boxes, np_iou_max = self.execute(graph_fn, []) + self.assertAllEqual(np_sampled_boxes.shape, [10, 4]) + self.assertAllGreater(np_iou_max, 0.3) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/box_list_test.py b/workspace/virtuallab/object_detection/core/box_list_test.py new file mode 100644 index 0000000..c1389db --- /dev/null +++ b/workspace/virtuallab/object_detection/core/box_list_test.py @@ -0,0 +1,121 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.box_list.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import box_list +from object_detection.utils import test_case + + +class BoxListTest(test_case.TestCase): + """Tests for BoxList class.""" + + def test_num_boxes(self): + def graph_fn(): + data = tf.constant([[0, 0, 1, 1], [1, 1, 2, 3], [3, 4, 5, 5]], tf.float32) + boxes = box_list.BoxList(data) + return boxes.num_boxes() + num_boxes_out = self.execute(graph_fn, []) + self.assertEqual(num_boxes_out, 3) + + def test_get_correct_center_coordinates_and_sizes(self): + boxes = np.array([[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]], + np.float32) + def graph_fn(boxes): + boxes = box_list.BoxList(boxes) + centers_sizes = boxes.get_center_coordinates_and_sizes() + return centers_sizes + centers_sizes_out = self.execute(graph_fn, [boxes]) + expected_centers_sizes = [[15, 0.35], [12.5, 0.25], [10, 0.3], [5, 0.3]] + self.assertAllClose(centers_sizes_out, expected_centers_sizes) + + def test_create_box_list_with_dynamic_shape(self): + def graph_fn(): + data = tf.constant([[0, 0, 1, 1], [1, 1, 2, 3], [3, 4, 5, 5]], tf.float32) + indices = tf.reshape(tf.where(tf.greater([1, 0, 1], 0)), [-1]) + data = tf.gather(data, indices) + assert data.get_shape().as_list() == [None, 4] + boxes = box_list.BoxList(data) + return boxes.num_boxes() + num_boxes = self.execute(graph_fn, []) + self.assertEqual(num_boxes, 2) + + def test_transpose_coordinates(self): + boxes = np.array([[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]], + np.float32) + def graph_fn(boxes): + boxes = box_list.BoxList(boxes) + boxes.transpose_coordinates() + return boxes.get() + transpoded_boxes = self.execute(graph_fn, [boxes]) + expected_corners = [[10.0, 10.0, 15.0, 20.0], [0.1, 0.2, 0.4, 0.5]] + self.assertAllClose(transpoded_boxes, expected_corners) + + def test_box_list_invalid_inputs(self): + data0 = tf.constant([[[0, 0, 1, 1], [3, 4, 5, 5]]], tf.float32) + data1 = tf.constant([[0, 0, 1], [1, 1, 2], [3, 4, 5]], tf.float32) + data2 = tf.constant([[0, 0, 1], [1, 1, 2], [3, 4, 5]], tf.int32) + + with self.assertRaises(ValueError): + _ = box_list.BoxList(data0) + with self.assertRaises(ValueError): + _ = box_list.BoxList(data1) + with self.assertRaises(ValueError): + _ = box_list.BoxList(data2) + + def test_num_boxes_static(self): + box_corners = [[10.0, 10.0, 20.0, 15.0], [0.2, 0.1, 0.5, 0.4]] + boxes = box_list.BoxList(tf.constant(box_corners)) + self.assertEqual(boxes.num_boxes_static(), 2) + self.assertEqual(type(boxes.num_boxes_static()), int) + + def test_as_tensor_dict(self): + boxes = tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]], + tf.float32) + boxlist = box_list.BoxList(boxes) + classes = tf.constant([0, 1]) + boxlist.add_field('classes', classes) + scores = tf.constant([0.75, 0.2]) + boxlist.add_field('scores', scores) + tensor_dict = boxlist.as_tensor_dict() + + self.assertDictEqual(tensor_dict, {'scores': scores, 'classes': classes, + 'boxes': boxes}) + + def test_as_tensor_dict_with_features(self): + boxes = tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]], + tf.float32) + boxlist = box_list.BoxList(boxes) + classes = tf.constant([0, 1]) + boxlist.add_field('classes', classes) + scores = tf.constant([0.75, 0.2]) + boxlist.add_field('scores', scores) + tensor_dict = boxlist.as_tensor_dict(['scores', 'classes']) + + self.assertDictEqual(tensor_dict, {'scores': scores, 'classes': classes}) + + def test_as_tensor_dict_missing_field(self): + boxlist = box_list.BoxList( + tf.constant([[0.1, 0.1, 0.4, 0.4], [0.1, 0.1, 0.5, 0.5]], tf.float32)) + boxlist.add_field('classes', tf.constant([0, 1])) + boxlist.add_field('scores', tf.constant([0.75, 0.2])) + with self.assertRaises(ValueError): + boxlist.as_tensor_dict(['foo', 'bar']) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/box_predictor.py b/workspace/virtuallab/object_detection/core/box_predictor.py new file mode 100644 index 0000000..27d77d2 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/box_predictor.py @@ -0,0 +1,227 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Box predictor for object detectors. + +Box predictors are classes that take a high level +image feature map as input and produce two predictions, +(1) a tensor encoding box locations, and +(2) a tensor encoding classes for each box. + +These components are passed directly to loss functions +in our detection models. + +These modules are separated from the main model since the same +few box predictor architectures are shared across many models. +""" +from abc import abstractmethod +import tensorflow.compat.v1 as tf + +BOX_ENCODINGS = 'box_encodings' +CLASS_PREDICTIONS_WITH_BACKGROUND = 'class_predictions_with_background' +MASK_PREDICTIONS = 'mask_predictions' + + +class BoxPredictor(object): + """BoxPredictor.""" + + def __init__(self, is_training, num_classes): + """Constructor. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + """ + self._is_training = is_training + self._num_classes = num_classes + + @property + def is_keras_model(self): + return False + + @property + def num_classes(self): + return self._num_classes + + def predict(self, image_features, num_predictions_per_location, + scope=None, **params): + """Computes encoded object locations and corresponding confidences. + + Takes a list of high level image feature maps as input and produces a list + of box encodings and a list of class scores where each element in the output + lists correspond to the feature maps in the input list. + + Args: + image_features: A list of float tensors of shape [batch_size, height_i, + width_i, channels_i] containing features for a batch of images. + num_predictions_per_location: A list of integers representing the number + of box predictions to be made per spatial location for each feature map. + scope: Variable and Op scope name. + **params: Additional keyword arguments for specific implementations of + BoxPredictor. + + Returns: + A dictionary containing at least the following tensors. + box_encodings: A list of float tensors. Each entry in the list + corresponds to a feature map in the input `image_features` list. All + tensors in the list have one of the two following shapes: + a. [batch_size, num_anchors_i, q, code_size] representing the location + of the objects, where q is 1 or the number of classes. + b. [batch_size, num_anchors_i, code_size]. + class_predictions_with_background: A list of float tensors of shape + [batch_size, num_anchors_i, num_classes + 1] representing the class + predictions for the proposals. Each entry in the list corresponds to a + feature map in the input `image_features` list. + + Raises: + ValueError: If length of `image_features` is not equal to length of + `num_predictions_per_location`. + """ + if len(image_features) != len(num_predictions_per_location): + raise ValueError('image_feature and num_predictions_per_location must ' + 'be of same length, found: {} vs {}'. + format(len(image_features), + len(num_predictions_per_location))) + if scope is not None: + with tf.variable_scope(scope): + return self._predict(image_features, num_predictions_per_location, + **params) + return self._predict(image_features, num_predictions_per_location, + **params) + + # TODO(rathodv): num_predictions_per_location could be moved to constructor. + # This is currently only used by ConvolutionalBoxPredictor. + @abstractmethod + def _predict(self, image_features, num_predictions_per_location, **params): + """Implementations must override this method. + + Args: + image_features: A list of float tensors of shape [batch_size, height_i, + width_i, channels_i] containing features for a batch of images. + num_predictions_per_location: A list of integers representing the number + of box predictions to be made per spatial location for each feature map. + **params: Additional keyword arguments for specific implementations of + BoxPredictor. + + Returns: + A dictionary containing at least the following tensors. + box_encodings: A list of float tensors. Each entry in the list + corresponds to a feature map in the input `image_features` list. All + tensors in the list have one of the two following shapes: + a. [batch_size, num_anchors_i, q, code_size] representing the location + of the objects, where q is 1 or the number of classes. + b. [batch_size, num_anchors_i, code_size]. + class_predictions_with_background: A list of float tensors of shape + [batch_size, num_anchors_i, num_classes + 1] representing the class + predictions for the proposals. Each entry in the list corresponds to a + feature map in the input `image_features` list. + """ + pass + + +class KerasBoxPredictor(tf.keras.layers.Layer): + """Keras-based BoxPredictor.""" + + def __init__(self, is_training, num_classes, freeze_batchnorm, + inplace_batchnorm_update, name=None): + """Constructor. + + Args: + is_training: Indicates whether the BoxPredictor is in training mode. + num_classes: number of classes. Note that num_classes *does not* + include the background category, so if groundtruth labels take values + in {0, 1, .., K-1}, num_classes=K (and not K+1, even though the + assigned classification targets can range from {0,... K}). + freeze_batchnorm: Whether to freeze batch norm parameters during + training or not. When training with a small batch size (e.g. 1), it is + desirable to freeze batch norm update and use pretrained batch norm + params. + inplace_batchnorm_update: Whether to update batch norm moving average + values inplace. When this is false train op must add a control + dependency on tf.graphkeys.UPDATE_OPS collection in order to update + batch norm statistics. + name: A string name scope to assign to the model. If `None`, Keras + will auto-generate one from the class name. + """ + super(KerasBoxPredictor, self).__init__(name=name) + + self._is_training = is_training + self._num_classes = num_classes + self._freeze_batchnorm = freeze_batchnorm + self._inplace_batchnorm_update = inplace_batchnorm_update + + @property + def is_keras_model(self): + return True + + @property + def num_classes(self): + return self._num_classes + + def call(self, image_features, **kwargs): + """Computes encoded object locations and corresponding confidences. + + Takes a list of high level image feature maps as input and produces a list + of box encodings and a list of class scores where each element in the output + lists correspond to the feature maps in the input list. + + Args: + image_features: A list of float tensors of shape [batch_size, height_i, + width_i, channels_i] containing features for a batch of images. + **kwargs: Additional keyword arguments for specific implementations of + BoxPredictor. + + Returns: + A dictionary containing at least the following tensors. + box_encodings: A list of float tensors. Each entry in the list + corresponds to a feature map in the input `image_features` list. All + tensors in the list have one of the two following shapes: + a. [batch_size, num_anchors_i, q, code_size] representing the location + of the objects, where q is 1 or the number of classes. + b. [batch_size, num_anchors_i, code_size]. + class_predictions_with_background: A list of float tensors of shape + [batch_size, num_anchors_i, num_classes + 1] representing the class + predictions for the proposals. Each entry in the list corresponds to a + feature map in the input `image_features` list. + """ + return self._predict(image_features, **kwargs) + + @abstractmethod + def _predict(self, image_features, **kwargs): + """Implementations must override this method. + + Args: + image_features: A list of float tensors of shape [batch_size, height_i, + width_i, channels_i] containing features for a batch of images. + **kwargs: Additional keyword arguments for specific implementations of + BoxPredictor. + + Returns: + A dictionary containing at least the following tensors. + box_encodings: A list of float tensors. Each entry in the list + corresponds to a feature map in the input `image_features` list. All + tensors in the list have one of the two following shapes: + a. [batch_size, num_anchors_i, q, code_size] representing the location + of the objects, where q is 1 or the number of classes. + b. [batch_size, num_anchors_i, code_size]. + class_predictions_with_background: A list of float tensors of shape + [batch_size, num_anchors_i, num_classes + 1] representing the class + predictions for the proposals. Each entry in the list corresponds to a + feature map in the input `image_features` list. + """ + raise NotImplementedError diff --git a/workspace/virtuallab/object_detection/core/box_predictor.pyc b/workspace/virtuallab/object_detection/core/box_predictor.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62da1a995ad74bab09860086afa93446eee1385f GIT binary patch literal 10672 zcmeHNTXP&o74F@Y6l-J0;o`)(Ks8iBDUQ5CR8bVF1dwGDTuRnvks{FO^_lIcCp^KE-M&D3TQzZ>GYO3v~YVf?Jl9t+?Q@itOdqG8WYWoQl z;rN1z7S#5lik?umt@;>wL4B;$M@s#4`$-ioD!ZU|FRI;3YWFF*-JagQOsZQ;Pjd3V zHk7)N{alZ88^xi|a@|MEc6M#(b!0vMWV!3K+eZdDZE~x_#5iZ2_Cw>fH+xnaJ&XrK zo!EVwwBwN(SlzeAPcV37#@aX?r{js&CXFy-7EMB{{XtgE9%rdrZZECA1VY}X4xHFD z%%V6Q=p8VbWTEjgh!omO-#%?%#TD0KO*rI};Q!vxI1_!Yp20JF)8|2v}jr`LXH8O6mKFD9hMQs1w zN<~7P%dd(42AaEz&O(Kuh3K19Wa#2{OW8SUH1)Efw&&%vRh>ecMWH{&tqaORmFKHl zZPnXadV-X>kow%6u!SpZoO}b#J%jE+Lp@L`z(G?Ez(@ls&N1#+kkSSg>+=bf8C$3E zTh9k>O^&pVT>$CDDfDbj-%eo&*hQxghLEPsWzXt7U_A6#SC7b^bV^d`WE7MdxTbNk zgXgn;Whx!5yP2mJz+!4c^|dI&glXolm2|*V8YYvd(3sf{X|guR^225AG9CAIxpDc# z!|u!ubh9wUeUnVAt5MS*d}~>+F6&NbS#Mrn{cyR?z^j{jiAD(y^ybSL1q;RshuH+? z3?WSEz+mD*3Z;}q#(j#4X5mdfu&}Doq&he00ESN+z?_{8-8P?LIb zp(c8k3(d0rolQ?;tqx|(2aQUoR>n(!l_s}Y4C>RQYvfSkL-=Kq} zV4!dTJZSP@uch)IC@*HZ+dO5!M>6ae4VB`N7M;l6qRJ1b6NrSk0wTY^sv2D9f|x%H ze@=bUR9kOj@K4_1b0_bKUb$aF6dQ;H6I!{rNGNm}^QJLIf)o@5&KMy>Vo;c+{W!t{ zmH~iXzu=V|A&@TX6NKa4vi{#8#4IZ%LBPJgPMmUC`T2wB48LkzfnNOVNA{oqs@-erj*QZPFCmF-P zglk6`xS<(a{j(k8!(rg!_wBMCTBHR&h?h%0au7#|JMpp(hbB$ovx4|taGCnT30LeP z5F>~iEu=_VWD40+^jiE^J(!Jc9#q>`=P*v;GHvd3ZZX_JQcNf)hQdLYv}ci9q|>ms zj>JdO9Frf`rH}1Qtbsy{resNy9f-h+Ev}f>l+`T3s0;jvN_6xsW?@(XW?|Tb+^J+W zc4zvD;FV#+Yr?D^DYWyxXuwnt?+c}lUQd!5xk?vnlj)(^hhd~>ec^yvXMeghQQ4_n zC_mFVs=L#y$g3cV6N-mj zW3pu5o4{6CH&jEX4Be+lYKsi&{^@?PNA4|z$0Ir|UgA+t>d^$I38%yjf)gocUWPg& zgJ07he5m&w`mQ92!tPmgXXBoGUjnHST2U%K*A)P=EV`Ku z2Z02sfE6w=Cu5;-$-Tm*+38_%GnXCady#vK`{0{1{Jfpq+%rfO`QU1MW4a?0gL&TaeQ! zLI$LFj*!^H;j!mHUKqH(V~Awd`RIgkftRRr z6eTKZd}7R*E|{INRAbtC z6#34_;SBnKzhvnKP@Bu4`z&z<*3W{`k|awi!3#UAX6`e~zYp}69&0+!L5!_56Q5ll z*c5lLAFPw7uP_SGX$DtFy};A5z>tJ~0+-zT3cQQ>>7JAb^gQArgX7xi>9zp0p1y-i ze6WZ^7kEU7QC>`(AVZh`Fe<&T;^r(0JXQc;?D;L47Bo~kq?oMf2s3%cY5VF7xa4F%uij5jpXIcF-O=VdUG}|P{5_Xfn4GKo-I(U9t)b@ z^Xy(=Cx8_FVDCkC#ElB1>YF@5JA06d#n-RVDv+vm@dbcXbBix72{U9%-UXSRP#EyU z_=-2BcwKwY;g@m7clK8$Y7}SkMrePvN0>`KDZ6p!o#0k?ynXxLkAoZQ8=J57?!4K(DV5tB>sy=kK{7}2B2>^dc`GEJx5%Bu v90Hn*` literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/class_agnostic_nms_test.py b/workspace/virtuallab/object_detection/core/class_agnostic_nms_test.py new file mode 100644 index 0000000..ed205c5 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/class_agnostic_nms_test.py @@ -0,0 +1,144 @@ +# Copyright 2019 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for google3.third_party.tensorflow_models.object_detection.core.class_agnostic_nms.""" +from absl.testing import parameterized +import tensorflow.compat.v1 as tf +from object_detection.core import post_processing +from object_detection.core import standard_fields as fields +from object_detection.utils import test_case + + +class ClassAgnosticNonMaxSuppressionTest(test_case.TestCase, + parameterized.TestCase): + + def test_class_agnostic_nms_select_with_shared_boxes(self): + def graph_fn(): + boxes = tf.constant( + [[[0, 0, 1, 1]], [[0, 0.1, 1, 1.1]], [[0, -0.1, 1, 0.9]], + [[0, 10, 1, 11]], [[0, 10.1, 1, 11.1]], [[0, 100, 1, 101]], + [[0, 1000, 1, 1002]], [[0, 1000, 1, 1002.1]]], tf.float32) + scores = tf.constant([[.9, 0.01], [.75, 0.05], [.6, 0.01], [.95, 0], + [.5, 0.01], [.3, 0.01], [.01, .85], [.01, .5]]) + score_thresh = 0.1 + iou_thresh = .5 + max_classes_per_detection = 1 + max_output_size = 4 + nms, _ = post_processing.class_agnostic_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_classes_per_detection, + max_output_size) + return (nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)) + + exp_nms_corners = [[0, 10, 1, 11], [0, 0, 1, 1], [0, 1000, 1, 1002], + [0, 100, 1, 101]] + exp_nms_scores = [.95, .9, .85, .3] + exp_nms_classes = [0, 0, 1, 0] + + (nms_corners_output, nms_scores_output, + nms_classes_output) = self.execute_cpu(graph_fn, []) + self.assertAllClose(nms_corners_output, exp_nms_corners) + self.assertAllClose(nms_scores_output, exp_nms_scores) + self.assertAllClose(nms_classes_output, exp_nms_classes) + + + def test_class_agnostic_nms_select_with_per_class_boxes(self): + def graph_fn(): + boxes = tf.constant( + [[[4, 5, 9, 10], [0, 0, 1, 1]], + [[0, 0.1, 1, 1.1], [4, 5, 9, 10]], + [[0, -0.1, 1, 0.9], [4, 5, 9, 10]], + [[0, 10, 1, 11], [4, 5, 9, 10]], + [[0, 10.1, 1, 11.1], [4, 5, 9, 10]], + [[0, 100, 1, 101], [4, 5, 9, 10]], + [[4, 5, 9, 10], [0, 1000, 1, 1002]], + [[4, 5, 9, 10], [0, 1000, 1, 1002.1]]], tf.float32) + scores = tf.constant([[.01, 0.9], + [.75, 0.05], + [.6, 0.01], + [.95, 0], + [.5, 0.01], + [.3, 0.01], + [.01, .85], + [.01, .5]]) + score_thresh = 0.1 + iou_thresh = .5 + max_classes_per_detection = 1 + max_output_size = 4 + nms, _ = post_processing.class_agnostic_non_max_suppression( + boxes, scores, score_thresh, iou_thresh, max_classes_per_detection, + max_output_size) + return (nms.get(), nms.get_field(fields.BoxListFields.scores), + nms.get_field(fields.BoxListFields.classes)) + (nms_corners_output, nms_scores_output, + nms_classes_output) = self.execute_cpu(graph_fn, []) + exp_nms_corners = [[0, 10, 1, 11], + [0, 0, 1, 1], + [0, 1000, 1, 1002], + [0, 100, 1, 101]] + exp_nms_scores = [.95, .9, .85, .3] + exp_nms_classes = [0, 1, 1, 0] + self.assertAllClose(nms_corners_output, exp_nms_corners) + self.assertAllClose(nms_scores_output, exp_nms_scores) + self.assertAllClose(nms_classes_output, exp_nms_classes) + + # Two cases will be tested here: using / not using static shapes. + # Named the two test cases for easier control during testing, with a flag of + # '--test_filter=ClassAgnosticNonMaxSuppressionTest.test_batch_classagnostic_nms_with_batch_size_1' + # or + # '--test_filter=ClassAgnosticNonMaxSuppressionTest.test_batch_classagnostic_nms_with_batch_size_1_use_static_shapes'. + @parameterized.named_parameters(('', False), ('_use_static_shapes', True)) + def test_batch_classagnostic_nms_with_batch_size_1(self, + use_static_shapes=False): + def graph_fn(): + boxes = tf.constant( + [[[[0, 0, 1, 1]], [[0, 0.1, 1, 1.1]], [[0, -0.1, 1, 0.9]], + [[0, 10, 1, 11]], [[0, 10.1, 1, 11.1]], [[0, 100, 1, 101]], + [[0, 1000, 1, 1002]], [[0, 1000, 1, 1002.1]]]], tf.float32) + scores = tf.constant([[[.9, 0.01], [.75, 0.05], [.6, 0.01], [.95, 0], + [.5, 0.01], [.3, 0.01], [.01, .85], [.01, .5]]]) + score_thresh = 0.1 + iou_thresh = .5 + max_output_size = 4 + max_classes_per_detection = 1 + use_class_agnostic_nms = True + (nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks, + nmsed_additional_fields, + num_detections) = post_processing.batch_multiclass_non_max_suppression( + boxes, + scores, + score_thresh, + iou_thresh, + max_size_per_class=max_output_size, + max_total_size=max_output_size, + use_class_agnostic_nms=use_class_agnostic_nms, + use_static_shapes=use_static_shapes, + max_classes_per_detection=max_classes_per_detection) + self.assertIsNone(nmsed_masks) + self.assertIsNone(nmsed_additional_fields) + return (nmsed_boxes, nmsed_scores, nmsed_classes, num_detections) + exp_nms_corners = [[[0, 10, 1, 11], [0, 0, 1, 1], [0, 1000, 1, 1002], + [0, 100, 1, 101]]] + exp_nms_scores = [[.95, .9, .85, .3]] + exp_nms_classes = [[0, 0, 1, 0]] + (nmsed_boxes, nmsed_scores, nmsed_classes, + num_detections) = self.execute_cpu(graph_fn, []) + self.assertAllClose(nmsed_boxes, exp_nms_corners) + self.assertAllClose(nmsed_scores, exp_nms_scores) + self.assertAllClose(nmsed_classes, exp_nms_classes) + self.assertEqual(num_detections, [4]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/data_decoder.py b/workspace/virtuallab/object_detection/core/data_decoder.py new file mode 100644 index 0000000..87ddf72 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/data_decoder.py @@ -0,0 +1,44 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Interface for data decoders. + +Data decoders decode the input data and return a dictionary of tensors keyed by +the entries in core.reader.Fields. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from abc import ABCMeta +from abc import abstractmethod +import six + + +class DataDecoder(six.with_metaclass(ABCMeta, object)): + """Interface for data decoders.""" + + @abstractmethod + def decode(self, data): + """Return a single image and associated labels. + + Args: + data: a string tensor holding a serialized protocol buffer corresponding + to data for a single image. + + Returns: + tensor_dict: a dictionary containing tensors. Possible keys are defined in + reader.Fields. + """ + pass diff --git a/workspace/virtuallab/object_detection/core/data_decoder.pyc b/workspace/virtuallab/object_detection/core/data_decoder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83a828560a9adf1299fcdd151b3a05e28c340428 GIT binary patch literal 1349 zcmb_by^a$x5S~qPn_TYrDJVg5N2;WwI3dLS2#JCd+N;>ccz0(nK6dTMc7V_x1r-u4 zZ^5(i0?gPUcQiDxQO2I}c;=g#&%>X0ra!)Zmo;p^5v|YPX5hcjrU58PX3z|v%b*`X zIe?7yVbq6E4wF8L`UuKV()Xa-gFc5cPkJ8pF_hz^kD;4DIe}&ZIDxBuz#MQ2*8^xN z`aXOHxCVGr9zZjOi`kUG_-`a$nt-lVHHy|c(WszAgSBnY`T2Buc3a;bh_FPV&1xNz zyE2V%7}m}Rx~uD;tx;|xY%2m9Z=DzKaD$DgHdDTeCOD0rLWtTroI6yMZT>=I*HBJP zpkS7f2=_>+%G+)oQ0jhVT}THLI%xEJ?I~5V+$GD(X%l3-HW5OykI8;~a{3B`x^bkK z!Kpg*7?!p<4+rGLm7b--ccUAS{e(yNh&D*0qfU|>01kPE+|rB(R9wv1)juThw|>^_ z#ec5ulmm}wLy@-kACtZK-AS*_Wk-$f)g=nhvdyv2r%p+IFH^w=FuJg+srxhF!Pega|ebnaM%dZ(-VI zPk46#(!?dxfN|WSLv2k^+T6hMbMe}GuPdsO2;zluNEBF#n`9sz)gUKP4$C^txk}87F09YbgsxRX+SbgeK a%=rHbh|PPN+V$)KCH9;>&+hMjhr?guU{Lq~ literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/data_parser.py b/workspace/virtuallab/object_detection/core/data_parser.py new file mode 100644 index 0000000..889545d --- /dev/null +++ b/workspace/virtuallab/object_detection/core/data_parser.py @@ -0,0 +1,45 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Interface for data parsers. + +Data parser parses input data and returns a dictionary of numpy arrays +keyed by the entries in standard_fields.py. Since the parser parses records +to numpy arrays (materialized tensors) directly, it is used to read data for +evaluation/visualization; to parse the data during training, DataDecoder should +be used. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from abc import ABCMeta +from abc import abstractmethod +import six + + +class DataToNumpyParser(six.with_metaclass(ABCMeta, object)): + """Abstract interface for data parser that produces numpy arrays.""" + + @abstractmethod + def parse(self, input_data): + """Parses input and returns a numpy array or a dictionary of numpy arrays. + + Args: + input_data: an input data + + Returns: + A numpy array or a dictionary of numpy arrays or None, if input + cannot be parsed. + """ + pass diff --git a/workspace/virtuallab/object_detection/core/densepose_ops.py b/workspace/virtuallab/object_detection/core/densepose_ops.py new file mode 100644 index 0000000..8dd8f39 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/densepose_ops.py @@ -0,0 +1,380 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""DensePose operations. + +DensePose part ids are represented as tensors of shape +[num_instances, num_points] and coordinates are represented as tensors of shape +[num_instances, num_points, 4] where each point holds (y, x, v, u). The location +of the DensePose sampled point is (y, x) in normalized coordinates. The surface +coordinate (in the part coordinate frame) is (v, u). Note that dim 1 of both +tensors may contain padding, since the number of sampled points per instance +is not fixed. The value `num_points` represents the maximum number of sampled +points for an instance in the example. +""" +import os + +import numpy as np +import scipy.io +import tensorflow.compat.v1 as tf + +from object_detection.utils import shape_utils + +PART_NAMES = [ + b'torso_back', b'torso_front', b'right_hand', b'left_hand', b'left_foot', + b'right_foot', b'right_upper_leg_back', b'left_upper_leg_back', + b'right_upper_leg_front', b'left_upper_leg_front', b'right_lower_leg_back', + b'left_lower_leg_back', b'right_lower_leg_front', b'left_lower_leg_front', + b'left_upper_arm_back', b'right_upper_arm_back', b'left_upper_arm_front', + b'right_upper_arm_front', b'left_lower_arm_back', b'right_lower_arm_back', + b'left_lower_arm_front', b'right_lower_arm_front', b'right_face', + b'left_face', +] + + +def scale(dp_surface_coords, y_scale, x_scale, scope=None): + """Scales DensePose coordinates in y and x dimensions. + + Args: + dp_surface_coords: a tensor of shape [num_instances, num_points, 4], with + coordinates in (y, x, v, u) format. + y_scale: (float) scalar tensor + x_scale: (float) scalar tensor + scope: name scope. + + Returns: + new_dp_surface_coords: a tensor of shape [num_instances, num_points, 4] + """ + with tf.name_scope(scope, 'DensePoseScale'): + y_scale = tf.cast(y_scale, tf.float32) + x_scale = tf.cast(x_scale, tf.float32) + new_keypoints = dp_surface_coords * [[[y_scale, x_scale, 1, 1]]] + return new_keypoints + + +def clip_to_window(dp_surface_coords, window, scope=None): + """Clips DensePose points to a window. + + This op clips any input DensePose points to a window. + + Args: + dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose surface coordinates in (y, x, v, u) format. + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window to which the op should clip the keypoints. + scope: name scope. + + Returns: + new_dp_surface_coords: a tensor of shape [num_instances, num_points, 4]. + """ + with tf.name_scope(scope, 'DensePoseClipToWindow'): + y, x, v, u = tf.split(value=dp_surface_coords, num_or_size_splits=4, axis=2) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + y = tf.maximum(tf.minimum(y, win_y_max), win_y_min) + x = tf.maximum(tf.minimum(x, win_x_max), win_x_min) + new_dp_surface_coords = tf.concat([y, x, v, u], 2) + return new_dp_surface_coords + + +def prune_outside_window(dp_num_points, dp_part_ids, dp_surface_coords, window, + scope=None): + """Prunes DensePose points that fall outside a given window. + + This function replaces points that fall outside the given window with zeros. + See also clip_to_window which clips any DensePose points that fall outside the + given window. + + Note that this operation uses dynamic shapes, and therefore is not currently + suitable for TPU. + + Args: + dp_num_points: a tensor of shape [num_instances] that indicates how many + (non-padded) DensePose points there are per instance. + dp_part_ids: a tensor of shape [num_instances, num_points] with DensePose + part ids. These part_ids are 0-indexed, where the first non-background + part has index 0. + dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose surface coordinates in (y, x, v, u) format. + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window outside of which the op should prune the points. + scope: name scope. + + Returns: + new_dp_num_points: a tensor of shape [num_instances] that indicates how many + (non-padded) DensePose points there are per instance after pruning. + new_dp_part_ids: a tensor of shape [num_instances, num_points] with + DensePose part ids. These part_ids are 0-indexed, where the first + non-background part has index 0. + new_dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose surface coordinates after pruning. + """ + with tf.name_scope(scope, 'DensePosePruneOutsideWindow'): + y, x, _, _ = tf.unstack(dp_surface_coords, axis=-1) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + + num_instances, num_points = shape_utils.combined_static_and_dynamic_shape( + dp_part_ids) + dp_num_points_tiled = tf.tile(dp_num_points[:, tf.newaxis], + multiples=[1, num_points]) + range_tiled = tf.tile(tf.range(num_points)[tf.newaxis, :], + multiples=[num_instances, 1]) + valid_initial = range_tiled < dp_num_points_tiled + valid_in_window = tf.logical_and( + tf.logical_and(y >= win_y_min, y <= win_y_max), + tf.logical_and(x >= win_x_min, x <= win_x_max)) + valid_indices = tf.logical_and(valid_initial, valid_in_window) + + new_dp_num_points = tf.math.reduce_sum( + tf.cast(valid_indices, tf.int32), axis=1) + max_num_points = tf.math.reduce_max(new_dp_num_points) + + def gather_and_reshuffle(elems): + dp_part_ids, dp_surface_coords, valid_indices = elems + locs = tf.where(valid_indices)[:, 0] + valid_part_ids = tf.gather(dp_part_ids, locs, axis=0) + valid_part_ids_padded = shape_utils.pad_or_clip_nd( + valid_part_ids, output_shape=[max_num_points]) + valid_surface_coords = tf.gather(dp_surface_coords, locs, axis=0) + valid_surface_coords_padded = shape_utils.pad_or_clip_nd( + valid_surface_coords, output_shape=[max_num_points, 4]) + return [valid_part_ids_padded, valid_surface_coords_padded] + + new_dp_part_ids, new_dp_surface_coords = ( + shape_utils.static_or_dynamic_map_fn( + gather_and_reshuffle, + elems=[dp_part_ids, dp_surface_coords, valid_indices], + dtype=[tf.int32, tf.float32], + back_prop=False)) + return new_dp_num_points, new_dp_part_ids, new_dp_surface_coords + + +def change_coordinate_frame(dp_surface_coords, window, scope=None): + """Changes coordinate frame of the points to be relative to window's frame. + + Given a window of the form [y_min, x_min, y_max, x_max] in normalized + coordinates, changes DensePose coordinates to be relative to this window. + + An example use case is data augmentation: where we are given groundtruth + points and would like to randomly crop the image to some window. In this + case we need to change the coordinate frame of each sampled point to be + relative to this new window. + + Args: + dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose surface coordinates in (y, x, v, u) format. + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window we should change the coordinate frame to. + scope: name scope. + + Returns: + new_dp_surface_coords: a tensor of shape [num_instances, num_points, 4]. + """ + with tf.name_scope(scope, 'DensePoseChangeCoordinateFrame'): + win_height = window[2] - window[0] + win_width = window[3] - window[1] + new_dp_surface_coords = scale( + dp_surface_coords - [window[0], window[1], 0, 0], + 1.0 / win_height, 1.0 / win_width) + return new_dp_surface_coords + + +def to_normalized_coordinates(dp_surface_coords, height, width, + check_range=True, scope=None): + """Converts absolute DensePose coordinates to normalized in range [0, 1]. + + This function raises an assertion failed error at graph execution time when + the maximum coordinate is smaller than 1.01 (which means that coordinates are + already normalized). The value 1.01 is to deal with small rounding errors. + + Args: + dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose absolute surface coordinates in (y, x, v, u) format. + height: Height of image. + width: Width of image. + check_range: If True, checks if the coordinates are already normalized. + scope: name scope. + + Returns: + A tensor of shape [num_instances, num_points, 4] with normalized + coordinates. + """ + with tf.name_scope(scope, 'DensePoseToNormalizedCoordinates'): + height = tf.cast(height, tf.float32) + width = tf.cast(width, tf.float32) + + if check_range: + max_val = tf.reduce_max(dp_surface_coords[:, :, :2]) + max_assert = tf.Assert(tf.greater(max_val, 1.01), + ['max value is lower than 1.01: ', max_val]) + with tf.control_dependencies([max_assert]): + width = tf.identity(width) + + return scale(dp_surface_coords, 1.0 / height, 1.0 / width) + + +def to_absolute_coordinates(dp_surface_coords, height, width, + check_range=True, scope=None): + """Converts normalized DensePose coordinates to absolute pixel coordinates. + + This function raises an assertion failed error when the maximum + coordinate value is larger than 1.01 (in which case coordinates are already + absolute). + + Args: + dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose normalized surface coordinates in (y, x, v, u) format. + height: Height of image. + width: Width of image. + check_range: If True, checks if the coordinates are normalized or not. + scope: name scope. + + Returns: + A tensor of shape [num_instances, num_points, 4] with absolute coordinates. + """ + with tf.name_scope(scope, 'DensePoseToAbsoluteCoordinates'): + height = tf.cast(height, tf.float32) + width = tf.cast(width, tf.float32) + + if check_range: + max_val = tf.reduce_max(dp_surface_coords[:, :, :2]) + max_assert = tf.Assert(tf.greater_equal(1.01, max_val), + ['maximum coordinate value is larger than 1.01: ', + max_val]) + with tf.control_dependencies([max_assert]): + width = tf.identity(width) + + return scale(dp_surface_coords, height, width) + + +class DensePoseHorizontalFlip(object): + """Class responsible for horizontal flipping of parts and surface coords.""" + + def __init__(self): + """Constructor.""" + + path = os.path.dirname(os.path.abspath(__file__)) + uv_symmetry_transforms_path = tf.resource_loader.get_path_to_datafile( + os.path.join(path, '..', 'dataset_tools', 'densepose', + 'UV_symmetry_transforms.mat')) + tf.logging.info('Loading DensePose symmetry transforms file from {}'.format( + uv_symmetry_transforms_path)) + with tf.io.gfile.GFile(uv_symmetry_transforms_path, 'rb') as f: + data = scipy.io.loadmat(f) + + # Create lookup maps which indicate how a VU coordinate changes after a + # horizontal flip. + uv_symmetry_map = {} + for key in ('U_transforms', 'V_transforms'): + uv_symmetry_map_per_part = [] + for i in range(data[key].shape[1]): + # The following tensor has shape [256, 256]. The raw data is stored as + # uint8 values, so convert to float and scale to the range [0., 1.] + data_normalized = data[key][0, i].astype(np.float32) / 255. + map_per_part = tf.constant(data_normalized, dtype=tf.float32) + uv_symmetry_map_per_part.append(map_per_part) + uv_symmetry_map[key] = tf.reshape( + tf.stack(uv_symmetry_map_per_part, axis=0), [-1]) + # The following dictionary contains flattened lookup maps for the U and V + # coordinates separately. The shape of each is [24 * 256 * 256]. + self.uv_symmetries = uv_symmetry_map + + # Create a list of that maps part index to flipped part index (0-indexed). + part_symmetries = [] + for i, part_name in enumerate(PART_NAMES): + if b'left' in part_name: + part_symmetries.append(PART_NAMES.index( + part_name.replace(b'left', b'right'))) + elif b'right' in part_name: + part_symmetries.append(PART_NAMES.index( + part_name.replace(b'right', b'left'))) + else: + part_symmetries.append(i) + self.part_symmetries = part_symmetries + + def flip_parts_and_coords(self, part_ids, vu): + """Flips part ids and coordinates. + + Args: + part_ids: a [num_instances, num_points] int32 tensor with pre-flipped part + ids. These part_ids are 0-indexed, where the first non-background part + has index 0. + vu: a [num_instances, num_points, 2] float32 tensor with pre-flipped vu + normalized coordinates. + + Returns: + new_part_ids: a [num_instances, num_points] int32 tensor with post-flipped + part ids. These part_ids are 0-indexed, where the first non-background + part has index 0. + new_vu: a [num_instances, num_points, 2] float32 tensor with post-flipped + vu coordinates. + """ + num_instances, num_points = shape_utils.combined_static_and_dynamic_shape( + part_ids) + part_ids_flattened = tf.reshape(part_ids, [-1]) + new_part_ids_flattened = tf.gather(self.part_symmetries, part_ids_flattened) + new_part_ids = tf.reshape(new_part_ids_flattened, + [num_instances, num_points]) + + # Convert VU floating point coordinates to values in [256, 256] grid. + vu = tf.math.minimum(tf.math.maximum(vu, 0.0), 1.0) + vu_locs = tf.cast(vu * 256., dtype=tf.int32) + vu_locs_flattened = tf.reshape(vu_locs, [-1, 2]) + v_locs_flattened, u_locs_flattened = tf.unstack(vu_locs_flattened, axis=1) + + # Convert vu_locs into lookup indices (in flattened part symmetries map). + symmetry_lookup_inds = ( + part_ids_flattened * 65536 + 256 * v_locs_flattened + u_locs_flattened) + + # New VU coordinates. + v_new = tf.gather(self.uv_symmetries['V_transforms'], symmetry_lookup_inds) + u_new = tf.gather(self.uv_symmetries['U_transforms'], symmetry_lookup_inds) + new_vu_flattened = tf.stack([v_new, u_new], axis=1) + new_vu = tf.reshape(new_vu_flattened, [num_instances, num_points, 2]) + + return new_part_ids, new_vu + + +def flip_horizontal(dp_part_ids, dp_surface_coords, scope=None): + """Flips the DensePose points horizontally around the flip_point. + + This operation flips dense pose annotations horizontally. Note that part ids + and surface coordinates may or may not change as a result of the flip. + + Args: + dp_part_ids: a tensor of shape [num_instances, num_points] with DensePose + part ids. These part_ids are 0-indexed, where the first non-background + part has index 0. + dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose surface coordinates in (y, x, v, u) normalized format. + scope: name scope. + + Returns: + new_dp_part_ids: a tensor of shape [num_instances, num_points] with + DensePose part ids after flipping. + new_dp_surface_coords: a tensor of shape [num_instances, num_points, 4] with + DensePose surface coordinates after flipping. + """ + with tf.name_scope(scope, 'DensePoseFlipHorizontal'): + # First flip x coordinate. + y, x, vu = tf.split(dp_surface_coords, num_or_size_splits=[1, 1, 2], axis=2) + xflipped = 1.0 - x + + # Flip part ids and surface coordinates. + horizontal_flip = DensePoseHorizontalFlip() + new_dp_part_ids, new_vu = horizontal_flip.flip_parts_and_coords( + dp_part_ids, vu) + new_dp_surface_coords = tf.concat([y, xflipped, new_vu], axis=2) + return new_dp_part_ids, new_dp_surface_coords + diff --git a/workspace/virtuallab/object_detection/core/densepose_ops.pyc b/workspace/virtuallab/object_detection/core/densepose_ops.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1da52add3caaf98c69f9c5c34b3efb8609755641 GIT binary patch literal 15394 zcmeHO+m9SqUOv@5o=bb|u{|@kCr+H>-DS5O>~WkdyU2<*F%C`^v6H06SqI0aQtqnm zu5oo$yQ+FF$;=DR!mflc2ni(63ULwezymAsz~i!l7bN%tctY@m#6u*+Gko9g)U9X6 z-o)b#`+)5^bLw1w=l1=sZvNMi)(?K`51fw5|D49}*YJ}bwUqLdx`otIUPFbJx`m9% zH`J|Wk)Kev4i))HwKk>Jrq!((DQT*(r3Q!9t;5RlCX|PElggV?-n8=2qNTjU%9~Z* z5#=3K-kkD|Det)QPAG3)c?-&0RNhJDol@RuLzx-^vctoj%VZ-}gnaDph!+^FNH7c@VIaS&zc9qmS*?!Llp(GpCQ6lFguX=y!L2b>q0+ zR6ZBb)btz;u?0@(_skUY#TdKF7x(F4h~Q8%Yt^HwFmr_Q#=djxDzE0KDypGmj@=cz z$aRx}S;p@5jaAgBWx1K6n%u0Tsww9%laCEmU*A|ojoNCYYL03sGdmC3jKwEXog}|H z6R13dAD-_u{G>f3nNn+(+7=vs&`=NGQWlFEYTJ^!rb6kC$)m7QE5xT_GZYf!8mc#; zdKUjJ>`bWb3AK9DYpQqNL$ccDMcR5O}igt52EYVOu8V!kF7xYGeN6*o89bhAO2-=+i`nw znQ>D~0po#c-kwl9O@+I>b4cw><~JV~{(+@fHKlf@)y|CCX-U9BKBFl{8ih`e}1F4m1cm{B~`9ck`HTe#xGQC53?kLdVR-&_5;mDf?{ulQ3xK(nlUUpoYn zQa%F97S8zomNN(>c)Q~2T9i7)BjW34%+;LRFL`75j7poTAkgIX5xLO_J5F(rKf5Udg?DR2Z2K5MEq z0D(anK)|fpIZ_}C5DL>wsP7myJgK@3wPf_6?IWt!;7mJ5^VXzmN}(KcFb|!n=$v=k zh6V(*G2Q0WHvSz|_mtW>CLN~pl%O=N?jgm$mbxd)@(J9DtAAiYF~$IgTS>vIWvsMMj%Fp69c)uR-2ds`3-f{sxz zp;A(f1j&gHvB;-ZMJ=Z@N)l+L;TFbEM?vP^4Si8oZoK`j%rVAEDh+5~9A|Z*MnP|t z9Rt<{036WMk2jzvMVyI$+EE-`q>|=)=l5Y~qM%Z9uIpw7!6C#@Rdk?R|9CRSRcQ*2 z)nqv(g`zIX9`yNQa8cY}yoeR}Pz51%3QauqZjhuI_Ak0fjjWf%qsXfd)`yBO-Smqk z$p@8s<#y$hR9EJwh6@+(3M%=2+<3@`DeTxhC$lY|o)`aL-#qPhGZ+cD3`i)3`kbp3 z*X2p>;-2$-!n>G{GkznDL1oWf+?N@DHsXy9sZzooe;@a3T}IiZ>?4@{mVsp>8rfe# zy|IXhFfkZ~SpZ{GD(X*%5(>3o@^h-?=^rA2{C^wLzNL1+ER4`DiZOVQ2ga+&b;07v zJE0Pg|AB*}1(>Q-JZM;8@j3g3jk2k}tEdc7r$S z1EsQKUbY2|nHxwLcZNwkY*S0F1Z?{}Kk=h73dfyza5sp2&w-qs1s$0(XS3!cWnW>m z^F{8)OQYy6b0u!n^W}8HxCf3Hva>XYiK1_ec)mC4K>8mI>}NS6?>8*KFTYf;#Rg`y z&*v22A`}D++@aHr?B|NE*CBlPZ3?^@&mWhf+Iag_l*rw%FEYo)*~?6*jEltY*e~(> zWhP%`@^h>aV?dqF8~a0Pt0%gK3MPTcz(_(dd2&s*oLGmS~PYI9Z_ zzh`mb&Y<)}V+P8{iN@UQBueIR3FnM)gK8IT9-^#lAd>--L>$f)5l30TCxFuue>K(8 zX}}t@6Q<-4OMqm+B(dYM39C_#EdId`aT;Pd)Raa3nox@m0Fa^w?6FA%(1)(UeB6Va zR$274Sm0@vqtH{x!Lhzee@2KGunv|E?HiyHZ+s;+%|&W_LuBj1mQjpAdz^lpVowWR z zZ9sm90BXc;BjjbtNGy-}UQndo5Nksi+>^b)75Cyn2sZ@4M7Av$xIL*#VJ0io4gF2p z|AUlEBpH}0^5HJQBFrkKYqk37B*bSk?t77yW2E|-$lCETiV10^924coar!68{vT(R zHK()h><2RnVM7Z$kldR6_cMz>s~mdJZR30y?JngdHqY)_>EF9fua{VRL}K8}R3i8E zkinl%UR`Oktj46ih|e|wWf!6gHe-vOzEA&9X_(mvysTftN%TAzM3TvvduxpP;;4j6 z1uOYejxENR#d)CL39AWQT(HC#GljYm1OE*lIR-+;GRByHEDXE=Hr6%87Gd%tj#Y*D zY^vmk(hLR|Fd>L_4UzilnI=nMJy?QVNO=!~niL2Lsc%Be8G?}%=tf7?H|rYH%&1C01cK8@?h;J3EhyH z?j`Q94>0sQ@Gzo2gSUpD7x8ui>T}~^jSkuiQaE)(C_&KcBYk=K#ml;FRK0=kM)ZSX zz@mTmFJPa z^|3WQ4$RCeyQ1He*G8r%*l!?4-rN;^lTU9~Rj2Pm`jlh6qQBYIHZeAstDn)`W{#w~gE)mT#w?Jg@p{Yqdm~;cBLFI3OIHNddgpIH{Lz26KY2AJ z4C1C~a}jU{#2;E+uPeHQyf$H?hIo{ZMbfn?oQs-tRc^b;-96kmXzQY`FdQI>LwKWx zJ`6cgCxG@djZQEEMp=+;*@B*gqRJkg<0S=fIX97BYsX|P3T5kFCV zFj!r}0I+ITTVF%h^zS(O0_Ru|uNlP8W%$nKta+uAL6lioOr z0#po;iY!?5F`+&=fD%e`PI3P8g2LJ!{xpMOjZE-2n6-bxSXi7x?RdrFKQ7qah_B}7 z^%xAK&b<$}tsrsNWkLMHW1-OY`Oc|a=OF0#_eXAM6G`nplX1ASDP-DfOyD}o4FMr@ z10+ug$HWE^JH@uuQS9F_OL`KCny;f+6ACnOEMwP_ue=#2!G{d*3a`WA*I_UIp_=~- z_(?Sqte+u@NX4x8F*4AG8;Y3;Wv`0toYE+8Ed*FYX-bEP+zSf7N57mycQMJ3@&`CM zjbjw5ym7yGx-8a6?l-x?HTRTydaaR zJ6ZxLMY3c|ak>FC*Wh#$i4#>8D`wT&5w!+i8oEiq!NVmnea=Z0Tz3$cG0FtwQ$`@* zq#^Nc{sm(zU%&H56m4-U;LhCa)5Zb=0c6~zH3UhvL_B`|&RYzT$jcF>2{$6mr)zNX zEvU6c4vxUhMZ{2$8hGar&IxBH@~pEESb-RdwNnx?0g~_kgSyGe)6!&#m5lg2%(jU8 zq;w4-UYwDy-2Y>BlUb#lXN1bbYV&&vX2$GUPW$?ukJN1l=Wx8j35u1@s(X_v`Mw1& zCMHCqwdYv4b~XENT0$&&_Q(GatdCgmY0p`4Hkyf&)@@$5zaS*3dkNN7$&7J|J`pp>DFN2XUUe6 zK@Lo5C`p%L7))t{`{meWBx8+@6|HsE8a^p{?vi+*KiZMlpycj~SeoChjVT4+t`&%8 zDT*c$LaBd^U$fuD@5%&^i)%vraCUikTAORL*O_b}X=@}hWo!(~Ls~TX9=sr7Xp-dd z`lWmt<&HyY9mklvX~e@bj7JHqp9s$Ka?uU;QrM5PK7FS&(y)(Evqq0B$A>AkhnOy~(>A-X3$N&XwxIRaW7)I;G=~DFM>H9Ea(pBq=*!cEU`-Zb}^;^HV zDjmgdm=7l&Xfbk%aXvqG4AbVovh36kyY@%?qEn@stA3MY7saUCl|I}|8MWRV=3{s> zltQ?S7?^j}ISddb6*5jB-8oK8I+e>sD|FG+{yoyg6$|;i3Qvinm`jAPmdtUcfw$?; zV-nRoYt29eo`n$Hf|cR)#H4iuR@bx5FTwPG9JwzyXPT4N5?ag_Y2&!D$I{5ma}x-+ zO9+RsiAPVYkx(*R1sEYv2(&hUMI!(0Vi`(Jcu= zgJEL^0|Ad)YT*>X>l9#<9Sqfg(8I7DODG4URq7S9Wd#CLP=UA({5vY)8O209b5aGW z5Q2gf`UMCxivc#nc5;l2V^uC~`fGSiWZwv^CT{_$IYts`iO?jN0WO&zjOye2BJ|PL-YEm zq_`&L)^vn?x5i>nHLS@S@>-v8(*Wefu9edl$91q>8y{|;T_fdBzH|E#zCPM5sN?h* zZ{H(m@K2ukJLgBsA|KbFVNV0NQUc z`B^3}FcI6uJItMD@;gj^8;Nl}{yuVIpAg8zCB>_QUuvZ9V!GC{% zhmBHn{n%?&6i(_@!d})#V7Bl86Z!Idt;;c%N`qa6rdO?S0WAxG1RdggqaoeQ#_hT8 zU|!*CBgt`CbHcR|sJxrPr^Fr@bje3APaqXHt-P4@CGrZ5T6LV>I0hTfSaaYH&w@jo znK;>80$(@-U!Dn>Hf%860)Vz*4u+qRl}L~yrvj4$$b+%XNZ4LJqKNxoW-WpG-7(MCng!_7yj&`D zg-@|ky5Ko;#qKMH6my1@_9}Z!`M<41KSP)sMQIF##Eqc)iwlp@W`(X8ZY}F7Qm#NT z;LsjzF8}{k{9i<{Bk-4m$QcI`Lb_aPi-<)j{h7F8drdLL)7;mqMF(~nBKQBXX*I9F z*#Fk}^nqaq+3lPc)vjYHkw~Kr+Su&t7>tr+)U88~heo&;e8w(JY|lqlL-V8}T(p?ykXLhL_7P8ids zPy~iUy{&p?!1G{e|0>)6HIuI)so?M+NGKC=bf=o_4uO%(1lI4yMXS}Efp~ucp%!fh z$Jl?vU*`W0LKNFrMu-n|&gJ#XqTHD;!-6jx zZIx_x_y2HNmj9?{#DL%Bc+{C!;>Z_koBefs*$$IVQAr<<4UeI+@3V*yZw57o^0)Y| zHcxT~+CeV+6~nr|!Ap=*5}CAS8`OkOG>$drzz1u8CuSFBmuBCXeSYo>bLURJ_rC|# Bs^1, 4->5 + [0, 9]] # 0->0, 8->9 + expected_flipped_surf_coords_yx = np.array([ + [[0.1, 1.0-0.7], [0.3, 1.0-0.8]], + [[0.0, 1.0-0.5], [0.6, 1.0-1.0]], + ], dtype=np.float32) + self.assertAllEqual(expected_flipped_part_ids, flipped_part_ids) + self.assertAllClose(expected_flipped_surf_coords_yx, + flipped_surf_coords[:, :, 0:2]) + self.assertAllEqual(part_ids_np, flipped_twice_part_ids) + self.assertAllClose(surf_coords_np, flipped_twice_surf_coords, rtol=1e-2, + atol=1e-2) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/freezable_batch_norm.py b/workspace/virtuallab/object_detection/core/freezable_batch_norm.py new file mode 100644 index 0000000..7f08fa5 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/freezable_batch_norm.py @@ -0,0 +1,68 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""A freezable batch norm layer that uses Keras batch normalization.""" +import tensorflow.compat.v1 as tf + + +class FreezableBatchNorm(tf.keras.layers.BatchNormalization): + """Batch normalization layer (Ioffe and Szegedy, 2014). + + This is a `freezable` batch norm layer that supports setting the `training` + parameter in the __init__ method rather than having to set it either via + the Keras learning phase or via the `call` method parameter. This layer will + forward all other parameters to the default Keras `BatchNormalization` + layer + + This is class is necessary because Object Detection model training sometimes + requires batch normalization layers to be `frozen` and used as if it was + evaluation time, despite still training (and potentially using dropout layers) + + Like the default Keras BatchNormalization layer, this will normalize the + activations of the previous layer at each batch, + i.e. applies a transformation that maintains the mean activation + close to 0 and the activation standard deviation close to 1. + + Arguments: + training: If False, the layer will normalize using the moving average and + std. dev, without updating the learned avg and std. dev. + If None or True, the layer will follow the keras BatchNormalization layer + strategy of checking the Keras learning phase at `call` time to decide + what to do. + **kwargs: The keyword arguments to forward to the keras BatchNormalization + layer constructor. + + Input shape: + Arbitrary. Use the keyword argument `input_shape` + (tuple of integers, does not include the samples axis) + when using this layer as the first layer in a model. + + Output shape: + Same shape as input. + + References: + - [Batch Normalization: Accelerating Deep Network Training by Reducing + Internal Covariate Shift](https://arxiv.org/abs/1502.03167) + """ + + def __init__(self, training=None, **kwargs): + super(FreezableBatchNorm, self).__init__(**kwargs) + self._training = training + + def call(self, inputs, training=None): + # Override the call arg only if the batchnorm is frozen. (Ignore None) + if self._training is False: # pylint: disable=g-bool-id-comparison + training = self._training + return super(FreezableBatchNorm, self).call(inputs, training=training) diff --git a/workspace/virtuallab/object_detection/core/freezable_batch_norm.pyc b/workspace/virtuallab/object_detection/core/freezable_batch_norm.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05af55562405516eae862363549c968bbc3b8c1d GIT binary patch literal 2834 zcmb_e%Wm676dg&9lNu>n1VuO9T%bUp1g>PKj{t5E#7PjqNZ|r zhEimtt7O}LKo|Xoep^4FJ@=B74LI3F5J%*2=046n=Z<>+e!Tb3pMOQMYCb*uKEma{ z$B-!X5@SatU6ps#ysPFt^>RlgJ!N*({F<8Ys`+)5V!@7jtJE8%etvm_SD*E-v5{j8 z$CNr_&h;cWG|?fRQDNPZa=kH*!b}IMyz%thIPEVj(0O*QLuQLX_8&f{;TCK?Yb`xy zL#ME_4ZMrjkJ;yK3HzQ3kMXZ;;s|MxC7ZsXdzZ366{*fcey zi-gY3&D12DeY$sW_|2Wc-X775S>_2}P2+8n<4Z|g&eB(9XrMec^i!Rf5P z(-_uD?ex+Fz>*d6Hj3~rL=jj*1q-?7f~acg_)d{Adq*JqvOIn1Tjt+h)C8_0^A?Q&1Sin+w3y2?Z2cD$|7 z7Ew-7tX@Vg&b1c^3lkgfwcF6d#2S>KA18AY2YL!bVkQDDZDMk25hHH_OSUu~cAfdH z${c7kd0!1a*G7E-$02mkU`^<^b z22+F#xNJaUhB;YeAF_pc2Sis9syFRpEjY!{i&Pju*$-11NUS6{Pc{{! zr8BF{R=(jH^kg(dCP3_COExeA(q)-vfCrU#y6~vyvPqEzxC93SE?@-SER8NMIf98e zx2Ol|d?2XtqKiO)3y@%TBnYIoy4`RnGC6kBY6+bD5sZPH4d;*OB&BCM_W+q~UQq5* z-ZiQcmlekA6*|RKBq`Pi0VD%uci`yyL`}WM-ZpB5NW({ z)zw&1o9A{d5iX=jy{~ZFaG+B&-Ee{8nTZ!|Anz0;;2+mQWVSKLiHWnsG{)9kAD*;z zxOeX^pv0*^f=Uf7%w}y_mo31&v=u9E8MgPu+&E}7NwF<_a8(?vlbW3rC4lrZU7EUT zM8|HDp>}RFpr4oz?+Ad#8Sjf^9}Av8{ZN%?{(u8CLl1QxqOxFPVbQsYIIoi0n%7G# z1WR9KP)@yhJu^ikEs;e{5@?p)rS%dAmaA z^u(^TgGdaW&9XH7+Mk6``lI1cyI0w2VBK`6Cw_SN?ZLgl!Pkf1-DenLKHGZD^_rUmE3vwP;rEw$7sc-{Bd%T2?_@Py0 zpFIUR8cm+c!uD0bQK@SF^>L?U@DFh}oS_*H=XNrbChdn-ns=16&3hPIXNKEDGLrTb z@jrn-@B^7WLJe=i^h%u3^??G z$(v5+tk338M~r~{Eg4CM{3y+5#3BROW+~2UgW*xFpQo+q>PH zJALNN=q851Z*U5F>r(W|L2Q?$4ujR9KvzGZI!a-Ne?lr;l(r;GMA2UU-(BsjQkM^( UlHmKGM)cvQh)S5Z+qu>IAHb$atpET3 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/freezable_batch_norm_tf2_test.py b/workspace/virtuallab/object_detection/core/freezable_batch_norm_tf2_test.py new file mode 100644 index 0000000..4cc42ae --- /dev/null +++ b/workspace/virtuallab/object_detection/core/freezable_batch_norm_tf2_test.py @@ -0,0 +1,198 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.freezable_batch_norm.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import unittest +import numpy as np +from six.moves import zip +import tensorflow.compat.v1 as tf + + +from object_detection.core import freezable_batch_norm +from object_detection.utils import tf_version + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class FreezableBatchNormTest(tf.test.TestCase): + """Tests for FreezableBatchNorm operations.""" + + def _build_model(self, training=None): + model = tf.keras.models.Sequential() + norm = freezable_batch_norm.FreezableBatchNorm(training=training, + input_shape=(10,), + momentum=0.8) + model.add(norm) + return model, norm + + def _copy_weights(self, source_weights, target_weights): + for source, target in zip(source_weights, target_weights): + target.assign(source) + + def _train_freezable_batch_norm(self, training_mean, training_var): + model, _ = self._build_model() + model.compile(loss='mse', optimizer='sgd') + + # centered on training_mean, variance training_var + train_data = np.random.normal( + loc=training_mean, + scale=training_var, + size=(1000, 10)) + model.fit(train_data, train_data, epochs=4, verbose=0) + return model.weights + + def _test_batchnorm_layer( + self, norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, training_mean, training_var): + out_tensor = norm(tf.convert_to_tensor(test_data, dtype=tf.float32), + training=training_arg) + out = out_tensor + out -= norm.beta + out /= norm.gamma + + if not should_be_training: + out *= training_var + out += (training_mean - testing_mean) + out /= testing_var + + np.testing.assert_allclose(out.numpy().mean(), 0.0, atol=1.5e-1) + np.testing.assert_allclose(out.numpy().std(), 1.0, atol=1.5e-1) + + def test_batchnorm_freezing_training_none(self): + training_mean = 5.0 + training_var = 10.0 + + testing_mean = -10.0 + testing_var = 5.0 + + # Initially train the batch norm, and save the weights + trained_weights = self._train_freezable_batch_norm(training_mean, + training_var) + + # Load the batch norm weights, freezing training to True. + # Apply the batch norm layer to testing data and ensure it is normalized + # according to the batch statistics. + model, norm = self._build_model(training=True) + self._copy_weights(trained_weights, model.weights) + + # centered on testing_mean, variance testing_var + test_data = np.random.normal( + loc=testing_mean, + scale=testing_var, + size=(1000, 10)) + + # Test with training=True passed to the call method: + training_arg = True + should_be_training = True + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + # Reset the weights, because they may have been updating by + # running with training=True + self._copy_weights(trained_weights, model.weights) + + # Test with training=False passed to the call method: + training_arg = False + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + # Test the layer in various Keras learning phase scopes: + training_arg = None + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + tf.keras.backend.set_learning_phase(True) + should_be_training = True + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + # Reset the weights, because they may have been updating by + # running with training=True + self._copy_weights(trained_weights, model.weights) + + tf.keras.backend.set_learning_phase(False) + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + def test_batchnorm_freezing_training_false(self): + training_mean = 5.0 + training_var = 10.0 + + testing_mean = -10.0 + testing_var = 5.0 + + # Initially train the batch norm, and save the weights + trained_weights = self._train_freezable_batch_norm(training_mean, + training_var) + + # Load the batch norm back up, freezing training to False. + # Apply the batch norm layer to testing data and ensure it is normalized + # according to the training data's statistics. + model, norm = self._build_model(training=False) + self._copy_weights(trained_weights, model.weights) + + # centered on testing_mean, variance testing_var + test_data = np.random.normal( + loc=testing_mean, + scale=testing_var, + size=(1000, 10)) + + # Make sure that the layer is never training + # Test with training=True passed to the call method: + training_arg = True + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + # Test with training=False passed to the call method: + training_arg = False + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + # Test the layer in various Keras learning phase scopes: + training_arg = None + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + tf.keras.backend.set_learning_phase(True) + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + tf.keras.backend.set_learning_phase(False) + should_be_training = False + self._test_batchnorm_layer(norm, should_be_training, test_data, + testing_mean, testing_var, training_arg, + training_mean, training_var) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/keypoint_ops.py b/workspace/virtuallab/object_detection/core/keypoint_ops.py new file mode 100644 index 0000000..1b0c4cc --- /dev/null +++ b/workspace/virtuallab/object_detection/core/keypoint_ops.py @@ -0,0 +1,376 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Keypoint operations. + +Keypoints are represented as tensors of shape [num_instances, num_keypoints, 2], +where the last dimension holds rank 2 tensors of the form [y, x] representing +the coordinates of the keypoint. +""" +import numpy as np +import tensorflow.compat.v1 as tf + + +def scale(keypoints, y_scale, x_scale, scope=None): + """Scales keypoint coordinates in x and y dimensions. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + y_scale: (float) scalar tensor + x_scale: (float) scalar tensor + scope: name scope. + + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'Scale'): + y_scale = tf.cast(y_scale, tf.float32) + x_scale = tf.cast(x_scale, tf.float32) + new_keypoints = keypoints * [[[y_scale, x_scale]]] + return new_keypoints + + +def clip_to_window(keypoints, window, scope=None): + """Clips keypoints to a window. + + This op clips any input keypoints to a window. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window to which the op should clip the keypoints. + scope: name scope. + + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'ClipToWindow'): + y, x = tf.split(value=keypoints, num_or_size_splits=2, axis=2) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + y = tf.maximum(tf.minimum(y, win_y_max), win_y_min) + x = tf.maximum(tf.minimum(x, win_x_max), win_x_min) + new_keypoints = tf.concat([y, x], 2) + return new_keypoints + + +def prune_outside_window(keypoints, window, scope=None): + """Prunes keypoints that fall outside a given window. + + This function replaces keypoints that fall outside the given window with nan. + See also clip_to_window which clips any keypoints that fall outside the given + window. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window outside of which the op should prune the keypoints. + scope: name scope. + + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'PruneOutsideWindow'): + y, x = tf.split(value=keypoints, num_or_size_splits=2, axis=2) + win_y_min, win_x_min, win_y_max, win_x_max = tf.unstack(window) + + valid_indices = tf.logical_and( + tf.logical_and(y >= win_y_min, y <= win_y_max), + tf.logical_and(x >= win_x_min, x <= win_x_max)) + + new_y = tf.where(valid_indices, y, np.nan * tf.ones_like(y)) + new_x = tf.where(valid_indices, x, np.nan * tf.ones_like(x)) + new_keypoints = tf.concat([new_y, new_x], 2) + + return new_keypoints + + +def change_coordinate_frame(keypoints, window, scope=None): + """Changes coordinate frame of the keypoints to be relative to window's frame. + + Given a window of the form [y_min, x_min, y_max, x_max], changes keypoint + coordinates from keypoints of shape [num_instances, num_keypoints, 2] + to be relative to this window. + + An example use case is data augmentation: where we are given groundtruth + keypoints and would like to randomly crop the image to some window. In this + case we need to change the coordinate frame of each groundtruth keypoint to be + relative to this new window. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + window: a tensor of shape [4] representing the [y_min, x_min, y_max, x_max] + window we should change the coordinate frame to. + scope: name scope. + + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'ChangeCoordinateFrame'): + win_height = window[2] - window[0] + win_width = window[3] - window[1] + new_keypoints = scale(keypoints - [window[0], window[1]], 1.0 / win_height, + 1.0 / win_width) + return new_keypoints + + +def keypoints_to_enclosing_bounding_boxes(keypoints): + """Creates enclosing bounding boxes from keypoints. + + Args: + keypoints: a [num_instances, num_keypoints, 2] float32 tensor with keypoints + in [y, x] format. + + Returns: + A [num_instances, 4] float32 tensor that tightly covers all the keypoints + for each instance. + """ + ymin = tf.math.reduce_min(keypoints[:, :, 0], axis=1) + xmin = tf.math.reduce_min(keypoints[:, :, 1], axis=1) + ymax = tf.math.reduce_max(keypoints[:, :, 0], axis=1) + xmax = tf.math.reduce_max(keypoints[:, :, 1], axis=1) + return tf.stack([ymin, xmin, ymax, xmax], axis=1) + + +def to_normalized_coordinates(keypoints, height, width, + check_range=True, scope=None): + """Converts absolute keypoint coordinates to normalized coordinates in [0, 1]. + + Usually one uses the dynamic shape of the image or conv-layer tensor: + keypoints = keypoint_ops.to_normalized_coordinates(keypoints, + tf.shape(images)[1], + tf.shape(images)[2]), + + This function raises an assertion failed error at graph execution time when + the maximum coordinate is smaller than 1.01 (which means that coordinates are + already normalized). The value 1.01 is to deal with small rounding errors. + + Args: + keypoints: A tensor of shape [num_instances, num_keypoints, 2]. + height: Maximum value for y coordinate of absolute keypoint coordinates. + width: Maximum value for x coordinate of absolute keypoint coordinates. + check_range: If True, checks if the coordinates are normalized. + scope: name scope. + + Returns: + tensor of shape [num_instances, num_keypoints, 2] with normalized + coordinates in [0, 1]. + """ + with tf.name_scope(scope, 'ToNormalizedCoordinates'): + height = tf.cast(height, tf.float32) + width = tf.cast(width, tf.float32) + + if check_range: + max_val = tf.reduce_max(keypoints) + max_assert = tf.Assert(tf.greater(max_val, 1.01), + ['max value is lower than 1.01: ', max_val]) + with tf.control_dependencies([max_assert]): + width = tf.identity(width) + + return scale(keypoints, 1.0 / height, 1.0 / width) + + +def to_absolute_coordinates(keypoints, height, width, + check_range=True, scope=None): + """Converts normalized keypoint coordinates to absolute pixel coordinates. + + This function raises an assertion failed error when the maximum keypoint + coordinate value is larger than 1.01 (in which case coordinates are already + absolute). + + Args: + keypoints: A tensor of shape [num_instances, num_keypoints, 2] + height: Maximum value for y coordinate of absolute keypoint coordinates. + width: Maximum value for x coordinate of absolute keypoint coordinates. + check_range: If True, checks if the coordinates are normalized or not. + scope: name scope. + + Returns: + tensor of shape [num_instances, num_keypoints, 2] with absolute coordinates + in terms of the image size. + + """ + with tf.name_scope(scope, 'ToAbsoluteCoordinates'): + height = tf.cast(height, tf.float32) + width = tf.cast(width, tf.float32) + + # Ensure range of input keypoints is correct. + if check_range: + max_val = tf.reduce_max(keypoints) + max_assert = tf.Assert(tf.greater_equal(1.01, max_val), + ['maximum keypoint coordinate value is larger ' + 'than 1.01: ', max_val]) + with tf.control_dependencies([max_assert]): + width = tf.identity(width) + + return scale(keypoints, height, width) + + +def flip_horizontal(keypoints, flip_point, flip_permutation=None, scope=None): + """Flips the keypoints horizontally around the flip_point. + + This operation flips the x coordinate for each keypoint around the flip_point + and also permutes the keypoints in a manner specified by flip_permutation. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + flip_point: (float) scalar tensor representing the x coordinate to flip the + keypoints around. + flip_permutation: integer list or rank 1 int32 tensor containing the + keypoint flip permutation. This specifies the mapping from original + keypoint indices to the flipped keypoint indices. This is used primarily + for keypoints that are not reflection invariant. E.g. Suppose there are 3 + keypoints representing ['head', 'right_eye', 'left_eye'], then a logical + choice for flip_permutation might be [0, 2, 1] since we want to swap the + 'left_eye' and 'right_eye' after a horizontal flip. + Default to None or empty list to keep the original order after flip. + scope: name scope. + + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'FlipHorizontal'): + keypoints = tf.transpose(keypoints, [1, 0, 2]) + if flip_permutation: + keypoints = tf.gather(keypoints, flip_permutation) + v, u = tf.split(value=keypoints, num_or_size_splits=2, axis=2) + u = flip_point * 2.0 - u + new_keypoints = tf.concat([v, u], 2) + new_keypoints = tf.transpose(new_keypoints, [1, 0, 2]) + return new_keypoints + + +def flip_vertical(keypoints, flip_point, flip_permutation=None, scope=None): + """Flips the keypoints vertically around the flip_point. + + This operation flips the y coordinate for each keypoint around the flip_point + and also permutes the keypoints in a manner specified by flip_permutation. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + flip_point: (float) scalar tensor representing the y coordinate to flip the + keypoints around. + flip_permutation: integer list or rank 1 int32 tensor containing the + keypoint flip permutation. This specifies the mapping from original + keypoint indices to the flipped keypoint indices. This is used primarily + for keypoints that are not reflection invariant. E.g. Suppose there are 3 + keypoints representing ['head', 'right_eye', 'left_eye'], then a logical + choice for flip_permutation might be [0, 2, 1] since we want to swap the + 'left_eye' and 'right_eye' after a horizontal flip. + Default to None or empty list to keep the original order after flip. + scope: name scope. + + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'FlipVertical'): + keypoints = tf.transpose(keypoints, [1, 0, 2]) + if flip_permutation: + keypoints = tf.gather(keypoints, flip_permutation) + v, u = tf.split(value=keypoints, num_or_size_splits=2, axis=2) + v = flip_point * 2.0 - v + new_keypoints = tf.concat([v, u], 2) + new_keypoints = tf.transpose(new_keypoints, [1, 0, 2]) + return new_keypoints + + +def rot90(keypoints, rotation_permutation=None, scope=None): + """Rotates the keypoints counter-clockwise by 90 degrees. + + Args: + keypoints: a tensor of shape [num_instances, num_keypoints, 2] + rotation_permutation: integer list or rank 1 int32 tensor containing the + keypoint flip permutation. This specifies the mapping from original + keypoint indices to the rotated keypoint indices. This is used primarily + for keypoints that are not rotation invariant. + Default to None or empty list to keep the original order after rotation. + scope: name scope. + Returns: + new_keypoints: a tensor of shape [num_instances, num_keypoints, 2] + """ + with tf.name_scope(scope, 'Rot90'): + keypoints = tf.transpose(keypoints, [1, 0, 2]) + if rotation_permutation: + keypoints = tf.gather(keypoints, rotation_permutation) + v, u = tf.split(value=keypoints[:, :, ::-1], num_or_size_splits=2, axis=2) + v = 1.0 - v + new_keypoints = tf.concat([v, u], 2) + new_keypoints = tf.transpose(new_keypoints, [1, 0, 2]) + return new_keypoints + + +def keypoint_weights_from_visibilities(keypoint_visibilities, + per_keypoint_weights=None): + """Returns a keypoint weights tensor. + + During training, it is often beneficial to consider only those keypoints that + are labeled. This function returns a weights tensor that combines default + per-keypoint weights, as well as the visibilities of individual keypoints. + + The returned tensor satisfies: + keypoint_weights[i, k] = per_keypoint_weights[k] * keypoint_visibilities[i, k] + where per_keypoint_weights[k] is set to 1 if not provided. + + Args: + keypoint_visibilities: A [num_instances, num_keypoints] boolean tensor + indicating whether a keypoint is labeled (and perhaps even visible). + per_keypoint_weights: A list or 1-d tensor of length `num_keypoints` with + per-keypoint weights. If None, will use 1 for each visible keypoint + weight. + + Returns: + A [num_instances, num_keypoints] float32 tensor with keypoint weights. Those + keypoints deemed visible will have the provided per-keypoint weight, and + all others will be set to zero. + """ + if per_keypoint_weights is None: + num_keypoints = keypoint_visibilities.shape.as_list()[1] + per_keypoint_weight_mult = tf.ones((1, num_keypoints,), dtype=tf.float32) + else: + per_keypoint_weight_mult = tf.expand_dims(per_keypoint_weights, axis=0) + return per_keypoint_weight_mult * tf.cast(keypoint_visibilities, tf.float32) + + +def set_keypoint_visibilities(keypoints, initial_keypoint_visibilities=None): + """Sets keypoint visibilities based on valid/invalid keypoints. + + Some keypoint operations set invisible keypoints (e.g. cropped keypoints) to + NaN, without affecting any keypoint "visibility" variables. This function is + used to update (or create) keypoint visibilities to agree with visible / + invisible keypoint coordinates. + + Args: + keypoints: a float32 tensor of shape [num_instances, num_keypoints, 2]. + initial_keypoint_visibilities: a boolean tensor of shape + [num_instances, num_keypoints]. If provided, will maintain the visibility + designation of a keypoint, so long as the corresponding coordinates are + not NaN. If not provided, will create keypoint visibilities directly from + the values in `keypoints` (i.e. NaN coordinates map to False, otherwise + they map to True). + + Returns: + keypoint_visibilities: a bool tensor of shape [num_instances, num_keypoints] + indicating whether a keypoint is visible or not. + """ + if initial_keypoint_visibilities is not None: + keypoint_visibilities = tf.cast(initial_keypoint_visibilities, tf.bool) + else: + keypoint_visibilities = tf.ones_like(keypoints[:, :, 0], dtype=tf.bool) + + keypoints_with_nan = tf.math.reduce_any(tf.math.is_nan(keypoints), axis=2) + keypoint_visibilities = tf.where( + keypoints_with_nan, + tf.zeros_like(keypoint_visibilities, dtype=tf.bool), + keypoint_visibilities) + return keypoint_visibilities diff --git a/workspace/virtuallab/object_detection/core/keypoint_ops.pyc b/workspace/virtuallab/object_detection/core/keypoint_ops.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae57c63a60baa3e0aa29be5293de0ad86c05f6ab GIT binary patch literal 15651 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 ARR910 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/keypoint_ops_test.py b/workspace/virtuallab/object_detection/core/keypoint_ops_test.py new file mode 100644 index 0000000..bbdcf01 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/keypoint_ops_test.py @@ -0,0 +1,365 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.keypoint_ops.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import keypoint_ops +from object_detection.utils import test_case + + +class KeypointOpsTest(test_case.TestCase): + """Tests for common keypoint operations.""" + + def test_scale(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.0, 0.0], [100.0, 200.0]], + [[50.0, 120.0], [100.0, 140.0]] + ]) + y_scale = tf.constant(1.0 / 100) + x_scale = tf.constant(1.0 / 200) + + expected_keypoints = tf.constant([ + [[0., 0.], [1.0, 1.0]], + [[0.5, 0.6], [1.0, 0.7]] + ]) + output = keypoint_ops.scale(keypoints, y_scale, x_scale) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_clip_to_window(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.0], [1.0, 1.0]] + ]) + window = tf.constant([0.25, 0.25, 0.75, 0.75]) + + expected_keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.25], [0.75, 0.75]] + ]) + output = keypoint_ops.clip_to_window(keypoints, window) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_prune_outside_window(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.0], [1.0, 1.0]] + ]) + window = tf.constant([0.25, 0.25, 0.75, 0.75]) + + expected_keypoints = tf.constant([[[0.25, 0.5], [0.75, 0.75]], + [[np.nan, np.nan], [np.nan, np.nan]]]) + output = keypoint_ops.prune_outside_window(keypoints, window) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_change_coordinate_frame(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.0], [1.0, 1.0]] + ]) + window = tf.constant([0.25, 0.25, 0.75, 0.75]) + + expected_keypoints = tf.constant([ + [[0, 0.5], [1.0, 1.0]], + [[0.5, -0.5], [1.5, 1.5]] + ]) + output = keypoint_ops.change_coordinate_frame(keypoints, window) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_keypoints_to_enclosing_bounding_boxes(self): + def graph_fn(): + keypoints = tf.constant( + [ + [ # Instance 0. + [5., 10.], + [3., 20.], + [8., 4.], + ], + [ # Instance 1. + [2., 12.], + [0., 3.], + [5., 19.], + ], + ], dtype=tf.float32) + bboxes = keypoint_ops.keypoints_to_enclosing_bounding_boxes(keypoints) + return bboxes + output = self.execute(graph_fn, []) + expected_bboxes = np.array( + [ + [3., 4., 8., 20.], + [0., 3., 5., 19.] + ]) + self.assertAllClose(expected_bboxes, output) + + def test_to_normalized_coordinates(self): + def graph_fn(): + keypoints = tf.constant([ + [[10., 30.], [30., 45.]], + [[20., 0.], [40., 60.]] + ]) + output = keypoint_ops.to_normalized_coordinates( + keypoints, 40, 60) + expected_keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.0], [1.0, 1.0]] + ]) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_to_normalized_coordinates_already_normalized(self): + if self.has_tpu(): return + def graph_fn(): + keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.0], [1.0, 1.0]] + ]) + output = keypoint_ops.to_normalized_coordinates( + keypoints, 40, 60) + return output + with self.assertRaisesOpError('assertion failed'): + self.execute_cpu(graph_fn, []) + + def test_to_absolute_coordinates(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.25, 0.5], [0.75, 0.75]], + [[0.5, 0.0], [1.0, 1.0]] + ]) + output = keypoint_ops.to_absolute_coordinates( + keypoints, 40, 60) + expected_keypoints = tf.constant([ + [[10., 30.], [30., 45.]], + [[20., 0.], [40., 60.]] + ]) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_to_absolute_coordinates_already_absolute(self): + if self.has_tpu(): return + def graph_fn(): + keypoints = tf.constant([ + [[10., 30.], [30., 45.]], + [[20., 0.], [40., 60.]] + ]) + output = keypoint_ops.to_absolute_coordinates( + keypoints, 40, 60) + return output + with self.assertRaisesOpError('assertion failed'): + self.execute_cpu(graph_fn, []) + + def test_flip_horizontal(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]] + ]) + expected_keypoints = tf.constant([ + [[0.1, 0.9], [0.2, 0.8], [0.3, 0.7]], + [[0.4, 0.6], [0.5, 0.5], [0.6, 0.4]], + ]) + output = keypoint_ops.flip_horizontal(keypoints, 0.5) + return output, expected_keypoints + + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_flip_horizontal_permutation(self): + + def graph_fn(): + keypoints = tf.constant([[[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]]]) + flip_permutation = [0, 2, 1] + + expected_keypoints = tf.constant([ + [[0.1, 0.9], [0.3, 0.7], [0.2, 0.8]], + [[0.4, 0.6], [0.6, 0.4], [0.5, 0.5]], + ]) + output = keypoint_ops.flip_horizontal(keypoints, 0.5, flip_permutation) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_flip_vertical(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]] + ]) + + expected_keypoints = tf.constant([ + [[0.9, 0.1], [0.8, 0.2], [0.7, 0.3]], + [[0.6, 0.4], [0.5, 0.5], [0.4, 0.6]], + ]) + output = keypoint_ops.flip_vertical(keypoints, 0.5) + return output, expected_keypoints + + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_flip_vertical_permutation(self): + + def graph_fn(): + keypoints = tf.constant([[[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.4], [0.5, 0.5], [0.6, 0.6]]]) + flip_permutation = [0, 2, 1] + + expected_keypoints = tf.constant([ + [[0.9, 0.1], [0.7, 0.3], [0.8, 0.2]], + [[0.6, 0.4], [0.4, 0.6], [0.5, 0.5]], + ]) + output = keypoint_ops.flip_vertical(keypoints, 0.5, flip_permutation) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_rot90(self): + def graph_fn(): + keypoints = tf.constant([ + [[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.6], [0.5, 0.6], [0.6, 0.7]] + ]) + expected_keypoints = tf.constant([ + [[0.9, 0.1], [0.8, 0.2], [0.7, 0.3]], + [[0.4, 0.4], [0.4, 0.5], [0.3, 0.6]], + ]) + output = keypoint_ops.rot90(keypoints) + return output, expected_keypoints + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_rot90_permutation(self): + + def graph_fn(): + keypoints = tf.constant([[[0.1, 0.1], [0.2, 0.2], [0.3, 0.3]], + [[0.4, 0.6], [0.5, 0.6], [0.6, 0.7]]]) + rot_permutation = [0, 2, 1] + expected_keypoints = tf.constant([ + [[0.9, 0.1], [0.7, 0.3], [0.8, 0.2]], + [[0.4, 0.4], [0.3, 0.6], [0.4, 0.5]], + ]) + output = keypoint_ops.rot90(keypoints, + rotation_permutation=rot_permutation) + return output, expected_keypoints + + output, expected_keypoints = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoints) + + def test_keypoint_weights_from_visibilities(self): + def graph_fn(): + keypoint_visibilities = tf.constant([ + [True, True, False], + [False, True, False] + ]) + per_keypoint_weights = [1.0, 2.0, 3.0] + keypoint_weights = keypoint_ops.keypoint_weights_from_visibilities( + keypoint_visibilities, per_keypoint_weights) + return keypoint_weights + expected_keypoint_weights = [ + [1.0, 2.0, 0.0], + [0.0, 2.0, 0.0] + ] + output = self.execute(graph_fn, []) + self.assertAllClose(output, expected_keypoint_weights) + + def test_keypoint_weights_from_visibilities_no_per_kpt_weights(self): + def graph_fn(): + keypoint_visibilities = tf.constant([ + [True, True, False], + [False, True, False] + ]) + keypoint_weights = keypoint_ops.keypoint_weights_from_visibilities( + keypoint_visibilities) + return keypoint_weights + expected_keypoint_weights = [ + [1.0, 1.0, 0.0], + [0.0, 1.0, 0.0] + ] + output = self.execute(graph_fn, []) + self.assertAllClose(expected_keypoint_weights, output) + + def test_set_keypoint_visibilities_no_initial_kpt_vis(self): + keypoints_np = np.array( + [ + [[np.nan, 0.2], + [np.nan, np.nan], + [-3., 7.]], + [[0.5, 0.2], + [4., 1.0], + [-3., np.nan]], + ], dtype=np.float32) + def graph_fn(): + keypoints = tf.constant(keypoints_np, dtype=tf.float32) + keypoint_visibilities = keypoint_ops.set_keypoint_visibilities( + keypoints) + return keypoint_visibilities + + expected_kpt_vis = [ + [False, False, True], + [True, True, False] + ] + output = self.execute(graph_fn, []) + self.assertAllEqual(expected_kpt_vis, output) + + def test_set_keypoint_visibilities(self): + keypoints_np = np.array( + [ + [[np.nan, 0.2], + [np.nan, np.nan], + [-3., 7.]], + [[0.5, 0.2], + [4., 1.0], + [-3., np.nan]], + ], dtype=np.float32) + initial_keypoint_visibilities_np = np.array( + [ + [False, + True, # Will be overriden by NaN coords. + False], # Will be maintained, even though non-NaN coords. + [True, + False, # Will be maintained, even though non-NaN coords. + False] + ]) + def graph_fn(): + keypoints = tf.constant(keypoints_np, dtype=tf.float32) + initial_keypoint_visibilities = tf.constant( + initial_keypoint_visibilities_np, dtype=tf.bool) + keypoint_visibilities = keypoint_ops.set_keypoint_visibilities( + keypoints, initial_keypoint_visibilities) + return keypoint_visibilities + + expected_kpt_vis = [ + [False, False, False], + [True, False, False] + ] + output = self.execute(graph_fn, []) + self.assertAllEqual(expected_kpt_vis, output) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/losses.py b/workspace/virtuallab/object_detection/core/losses.py new file mode 100644 index 0000000..434ab47 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/losses.py @@ -0,0 +1,808 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Classification and regression loss functions for object detection. + +Localization losses: + * WeightedL2LocalizationLoss + * WeightedSmoothL1LocalizationLoss + * WeightedIOULocalizationLoss + +Classification losses: + * WeightedSigmoidClassificationLoss + * WeightedSoftmaxClassificationLoss + * WeightedSoftmaxClassificationAgainstLogitsLoss + * BootstrappedSigmoidClassificationLoss +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import abc +import six +import tensorflow.compat.v1 as tf +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.utils import ops +from object_detection.utils import shape_utils + + +class Loss(six.with_metaclass(abc.ABCMeta, object)): + """Abstract base class for loss functions.""" + + def __call__(self, + prediction_tensor, + target_tensor, + ignore_nan_targets=False, + losses_mask=None, + scope=None, + **params): + """Call the loss function. + + Args: + prediction_tensor: an N-d tensor of shape [batch, anchors, ...] + representing predicted quantities. + target_tensor: an N-d tensor of shape [batch, anchors, ...] representing + regression or classification targets. + ignore_nan_targets: whether to ignore nan targets in the loss computation. + E.g. can be used if the target tensor is missing groundtruth data that + shouldn't be factored into the loss. + losses_mask: A [batch] boolean tensor that indicates whether losses should + be applied to individual images in the batch. For elements that + are False, corresponding prediction, target, and weight tensors will not + contribute to loss computation. If None, no filtering will take place + prior to loss computation. + scope: Op scope name. Defaults to 'Loss' if None. + **params: Additional keyword arguments for specific implementations of + the Loss. + + Returns: + loss: a tensor representing the value of the loss function. + """ + with tf.name_scope(scope, 'Loss', + [prediction_tensor, target_tensor, params]) as scope: + if ignore_nan_targets: + target_tensor = tf.where(tf.is_nan(target_tensor), + prediction_tensor, + target_tensor) + if losses_mask is not None: + tensor_multiplier = self._get_loss_multiplier_for_tensor( + prediction_tensor, + losses_mask) + prediction_tensor *= tensor_multiplier + target_tensor *= tensor_multiplier + + if 'weights' in params: + params['weights'] = tf.convert_to_tensor(params['weights']) + weights_multiplier = self._get_loss_multiplier_for_tensor( + params['weights'], + losses_mask) + params['weights'] *= weights_multiplier + return self._compute_loss(prediction_tensor, target_tensor, **params) + + def _get_loss_multiplier_for_tensor(self, tensor, losses_mask): + loss_multiplier_shape = tf.stack([-1] + [1] * (len(tensor.shape) - 1)) + return tf.cast(tf.reshape(losses_mask, loss_multiplier_shape), tf.float32) + + @abc.abstractmethod + def _compute_loss(self, prediction_tensor, target_tensor, **params): + """Method to be overridden by implementations. + + Args: + prediction_tensor: a tensor representing predicted quantities + target_tensor: a tensor representing regression or classification targets + **params: Additional keyword arguments for specific implementations of + the Loss. + + Returns: + loss: an N-d tensor of shape [batch, anchors, ...] containing the loss per + anchor + """ + pass + + +class WeightedL2LocalizationLoss(Loss): + """L2 localization loss function with anchorwise output support. + + Loss[b,a] = .5 * ||weights[b,a] * (prediction[b,a,:] - target[b,a,:])||^2 + """ + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + code_size] representing the (encoded) predicted locations of objects. + target_tensor: A float tensor of shape [batch_size, num_anchors, + code_size] representing the regression targets + weights: a float tensor of shape [batch_size, num_anchors] + + Returns: + loss: a float tensor of shape [batch_size, num_anchors] tensor + representing the value of the loss function. + """ + weighted_diff = (prediction_tensor - target_tensor) * tf.expand_dims( + weights, 2) + square_diff = 0.5 * tf.square(weighted_diff) + return tf.reduce_sum(square_diff, 2) + + +class WeightedSmoothL1LocalizationLoss(Loss): + """Smooth L1 localization loss function aka Huber Loss.. + + The smooth L1_loss is defined elementwise as .5 x^2 if |x| <= delta and + delta * (|x|- 0.5*delta) otherwise, where x is the difference between + predictions and target. + + See also Equation (3) in the Fast R-CNN paper by Ross Girshick (ICCV 2015) + """ + + def __init__(self, delta=1.0): + """Constructor. + + Args: + delta: delta for smooth L1 loss. + """ + super(WeightedSmoothL1LocalizationLoss, self).__init__() + self._delta = delta + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + code_size] representing the (encoded) predicted locations of objects. + target_tensor: A float tensor of shape [batch_size, num_anchors, + code_size] representing the regression targets + weights: a float tensor of shape [batch_size, num_anchors] + + Returns: + loss: a float tensor of shape [batch_size, num_anchors] tensor + representing the value of the loss function. + """ + return tf.reduce_sum(tf.losses.huber_loss( + target_tensor, + prediction_tensor, + delta=self._delta, + weights=tf.expand_dims(weights, axis=2), + loss_collection=None, + reduction=tf.losses.Reduction.NONE + ), axis=2) + + +class WeightedIOULocalizationLoss(Loss): + """IOU localization loss function. + + Sums the IOU for corresponding pairs of predicted/groundtruth boxes + and for each pair assign a loss of 1 - IOU. We then compute a weighted + sum over all pairs which is returned as the total loss. + """ + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, 4] + representing the decoded predicted boxes + target_tensor: A float tensor of shape [batch_size, num_anchors, 4] + representing the decoded target boxes + weights: a float tensor of shape [batch_size, num_anchors] + + Returns: + loss: a float tensor of shape [batch_size, num_anchors] tensor + representing the value of the loss function. + """ + predicted_boxes = box_list.BoxList(tf.reshape(prediction_tensor, [-1, 4])) + target_boxes = box_list.BoxList(tf.reshape(target_tensor, [-1, 4])) + per_anchor_iou_loss = 1.0 - box_list_ops.matched_iou(predicted_boxes, + target_boxes) + return tf.reshape(weights, [-1]) * per_anchor_iou_loss + + +class WeightedGIOULocalizationLoss(Loss): + """GIOU localization loss function. + + Sums the GIOU loss for corresponding pairs of predicted/groundtruth boxes + and for each pair assign a loss of 1 - GIOU. We then compute a weighted + sum over all pairs which is returned as the total loss. + """ + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, 4] + representing the decoded predicted boxes + target_tensor: A float tensor of shape [batch_size, num_anchors, 4] + representing the decoded target boxes + weights: a float tensor of shape [batch_size, num_anchors] + + Returns: + loss: a float tensor of shape [batch_size, num_anchors] tensor + representing the value of the loss function. + """ + batch_size, num_anchors, _ = shape_utils.combined_static_and_dynamic_shape( + prediction_tensor) + predicted_boxes = tf.reshape(prediction_tensor, [-1, 4]) + target_boxes = tf.reshape(target_tensor, [-1, 4]) + + per_anchor_iou_loss = 1 - ops.giou(predicted_boxes, target_boxes) + return tf.reshape(tf.reshape(weights, [-1]) * per_anchor_iou_loss, + [batch_size, num_anchors]) + + +class WeightedSigmoidClassificationLoss(Loss): + """Sigmoid cross entropy classification loss function.""" + + def _compute_loss(self, + prediction_tensor, + target_tensor, + weights, + class_indices=None): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing the predicted logits for each class + target_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing one-hot encoded classification targets + weights: a float tensor of shape, either [batch_size, num_anchors, + num_classes] or [batch_size, num_anchors, 1]. If the shape is + [batch_size, num_anchors, 1], all the classses are equally weighted. + class_indices: (Optional) A 1-D integer tensor of class indices. + If provided, computes loss only for the specified class indices. + + Returns: + loss: a float tensor of shape [batch_size, num_anchors, num_classes] + representing the value of the loss function. + """ + if class_indices is not None: + weights *= tf.reshape( + ops.indices_to_dense_vector(class_indices, + tf.shape(prediction_tensor)[2]), + [1, 1, -1]) + per_entry_cross_ent = (tf.nn.sigmoid_cross_entropy_with_logits( + labels=target_tensor, logits=prediction_tensor)) + return per_entry_cross_ent * weights + + +class SigmoidFocalClassificationLoss(Loss): + """Sigmoid focal cross entropy loss. + + Focal loss down-weights well classified examples and focusses on the hard + examples. See https://arxiv.org/pdf/1708.02002.pdf for the loss definition. + """ + + def __init__(self, gamma=2.0, alpha=0.25): + """Constructor. + + Args: + gamma: exponent of the modulating factor (1 - p_t) ^ gamma. + alpha: optional alpha weighting factor to balance positives vs negatives. + """ + super(SigmoidFocalClassificationLoss, self).__init__() + self._alpha = alpha + self._gamma = gamma + + def _compute_loss(self, + prediction_tensor, + target_tensor, + weights, + class_indices=None): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing the predicted logits for each class + target_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing one-hot encoded classification targets + weights: a float tensor of shape, either [batch_size, num_anchors, + num_classes] or [batch_size, num_anchors, 1]. If the shape is + [batch_size, num_anchors, 1], all the classses are equally weighted. + class_indices: (Optional) A 1-D integer tensor of class indices. + If provided, computes loss only for the specified class indices. + + Returns: + loss: a float tensor of shape [batch_size, num_anchors, num_classes] + representing the value of the loss function. + """ + if class_indices is not None: + weights *= tf.reshape( + ops.indices_to_dense_vector(class_indices, + tf.shape(prediction_tensor)[2]), + [1, 1, -1]) + per_entry_cross_ent = (tf.nn.sigmoid_cross_entropy_with_logits( + labels=target_tensor, logits=prediction_tensor)) + prediction_probabilities = tf.sigmoid(prediction_tensor) + p_t = ((target_tensor * prediction_probabilities) + + ((1 - target_tensor) * (1 - prediction_probabilities))) + modulating_factor = 1.0 + if self._gamma: + modulating_factor = tf.pow(1.0 - p_t, self._gamma) + alpha_weight_factor = 1.0 + if self._alpha is not None: + alpha_weight_factor = (target_tensor * self._alpha + + (1 - target_tensor) * (1 - self._alpha)) + focal_cross_entropy_loss = (modulating_factor * alpha_weight_factor * + per_entry_cross_ent) + return focal_cross_entropy_loss * weights + + +class WeightedSoftmaxClassificationLoss(Loss): + """Softmax loss function.""" + + def __init__(self, logit_scale=1.0): + """Constructor. + + Args: + logit_scale: When this value is high, the prediction is "diffused" and + when this value is low, the prediction is made peakier. + (default 1.0) + + """ + super(WeightedSoftmaxClassificationLoss, self).__init__() + self._logit_scale = logit_scale + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing the predicted logits for each class + target_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing one-hot encoded classification targets + weights: a float tensor of shape, either [batch_size, num_anchors, + num_classes] or [batch_size, num_anchors, 1]. If the shape is + [batch_size, num_anchors, 1], all the classses are equally weighted. + + Returns: + loss: a float tensor of shape [batch_size, num_anchors] + representing the value of the loss function. + """ + weights = tf.reduce_mean(weights, axis=2) + num_classes = prediction_tensor.get_shape().as_list()[-1] + prediction_tensor = tf.divide( + prediction_tensor, self._logit_scale, name='scale_logit') + per_row_cross_ent = (tf.nn.softmax_cross_entropy_with_logits( + labels=tf.reshape(target_tensor, [-1, num_classes]), + logits=tf.reshape(prediction_tensor, [-1, num_classes]))) + return tf.reshape(per_row_cross_ent, tf.shape(weights)) * weights + + +class WeightedSoftmaxClassificationAgainstLogitsLoss(Loss): + """Softmax loss function against logits. + + Targets are expected to be provided in logits space instead of "one hot" or + "probability distribution" space. + """ + + def __init__(self, logit_scale=1.0): + """Constructor. + + Args: + logit_scale: When this value is high, the target is "diffused" and + when this value is low, the target is made peakier. + (default 1.0) + + """ + super(WeightedSoftmaxClassificationAgainstLogitsLoss, self).__init__() + self._logit_scale = logit_scale + + def _scale_and_softmax_logits(self, logits): + """Scale logits then apply softmax.""" + scaled_logits = tf.divide(logits, self._logit_scale, name='scale_logits') + return tf.nn.softmax(scaled_logits, name='convert_scores') + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing the predicted logits for each class + target_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing logit classification targets + weights: a float tensor of shape, either [batch_size, num_anchors, + num_classes] or [batch_size, num_anchors, 1]. If the shape is + [batch_size, num_anchors, 1], all the classses are equally weighted. + + Returns: + loss: a float tensor of shape [batch_size, num_anchors] + representing the value of the loss function. + """ + weights = tf.reduce_mean(weights, axis=2) + num_classes = prediction_tensor.get_shape().as_list()[-1] + target_tensor = self._scale_and_softmax_logits(target_tensor) + prediction_tensor = tf.divide(prediction_tensor, self._logit_scale, + name='scale_logits') + + per_row_cross_ent = (tf.nn.softmax_cross_entropy_with_logits( + labels=tf.reshape(target_tensor, [-1, num_classes]), + logits=tf.reshape(prediction_tensor, [-1, num_classes]))) + return tf.reshape(per_row_cross_ent, tf.shape(weights)) * weights + + +class BootstrappedSigmoidClassificationLoss(Loss): + """Bootstrapped sigmoid cross entropy classification loss function. + + This loss uses a convex combination of training labels and the current model's + predictions as training targets in the classification loss. The idea is that + as the model improves over time, its predictions can be trusted more and we + can use these predictions to mitigate the damage of noisy/incorrect labels, + because incorrect labels are likely to be eventually highly inconsistent with + other stimuli predicted to have the same label by the model. + + In "soft" bootstrapping, we use all predicted class probabilities, whereas in + "hard" bootstrapping, we use the single class favored by the model. + + See also Training Deep Neural Networks On Noisy Labels with Bootstrapping by + Reed et al. (ICLR 2015). + """ + + def __init__(self, alpha, bootstrap_type='soft'): + """Constructor. + + Args: + alpha: a float32 scalar tensor between 0 and 1 representing interpolation + weight + bootstrap_type: set to either 'hard' or 'soft' (default) + + Raises: + ValueError: if bootstrap_type is not either 'hard' or 'soft' + """ + super(BootstrappedSigmoidClassificationLoss, self).__init__() + if bootstrap_type != 'hard' and bootstrap_type != 'soft': + raise ValueError('Unrecognized bootstrap_type: must be one of ' + '\'hard\' or \'soft.\'') + self._alpha = alpha + self._bootstrap_type = bootstrap_type + + def _compute_loss(self, prediction_tensor, target_tensor, weights): + """Compute loss function. + + Args: + prediction_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing the predicted logits for each class + target_tensor: A float tensor of shape [batch_size, num_anchors, + num_classes] representing one-hot encoded classification targets + weights: a float tensor of shape, either [batch_size, num_anchors, + num_classes] or [batch_size, num_anchors, 1]. If the shape is + [batch_size, num_anchors, 1], all the classses are equally weighted. + + Returns: + loss: a float tensor of shape [batch_size, num_anchors, num_classes] + representing the value of the loss function. + """ + if self._bootstrap_type == 'soft': + bootstrap_target_tensor = self._alpha * target_tensor + ( + 1.0 - self._alpha) * tf.sigmoid(prediction_tensor) + else: + bootstrap_target_tensor = self._alpha * target_tensor + ( + 1.0 - self._alpha) * tf.cast( + tf.sigmoid(prediction_tensor) > 0.5, tf.float32) + per_entry_cross_ent = (tf.nn.sigmoid_cross_entropy_with_logits( + labels=bootstrap_target_tensor, logits=prediction_tensor)) + return per_entry_cross_ent * weights + + +class HardExampleMiner(object): + """Hard example mining for regions in a list of images. + + Implements hard example mining to select a subset of regions to be + back-propagated. For each image, selects the regions with highest losses, + subject to the condition that a newly selected region cannot have + an IOU > iou_threshold with any of the previously selected regions. + This can be achieved by re-using a greedy non-maximum suppression algorithm. + A constraint on the number of negatives mined per positive region can also be + enforced. + + Reference papers: "Training Region-based Object Detectors with Online + Hard Example Mining" (CVPR 2016) by Srivastava et al., and + "SSD: Single Shot MultiBox Detector" (ECCV 2016) by Liu et al. + """ + + def __init__(self, + num_hard_examples=64, + iou_threshold=0.7, + loss_type='both', + cls_loss_weight=0.05, + loc_loss_weight=0.06, + max_negatives_per_positive=None, + min_negatives_per_image=0): + """Constructor. + + The hard example mining implemented by this class can replicate the behavior + in the two aforementioned papers (Srivastava et al., and Liu et al). + To replicate the A2 paper (Srivastava et al), num_hard_examples is set + to a fixed parameter (64 by default) and iou_threshold is set to .7 for + running non-max-suppression the predicted boxes prior to hard mining. + In order to replicate the SSD paper (Liu et al), num_hard_examples should + be set to None, max_negatives_per_positive should be 3 and iou_threshold + should be 1.0 (in order to effectively turn off NMS). + + Args: + num_hard_examples: maximum number of hard examples to be + selected per image (prior to enforcing max negative to positive ratio + constraint). If set to None, all examples obtained after NMS are + considered. + iou_threshold: minimum intersection over union for an example + to be discarded during NMS. + loss_type: use only classification losses ('cls', default), + localization losses ('loc') or both losses ('both'). + In the last case, cls_loss_weight and loc_loss_weight are used to + compute weighted sum of the two losses. + cls_loss_weight: weight for classification loss. + loc_loss_weight: weight for location loss. + max_negatives_per_positive: maximum number of negatives to retain for + each positive anchor. By default, num_negatives_per_positive is None, + which means that we do not enforce a prespecified negative:positive + ratio. Note also that num_negatives_per_positives can be a float + (and will be converted to be a float even if it is passed in otherwise). + min_negatives_per_image: minimum number of negative anchors to sample for + a given image. Setting this to a positive number allows sampling + negatives in an image without any positive anchors and thus not biased + towards at least one detection per image. + """ + self._num_hard_examples = num_hard_examples + self._iou_threshold = iou_threshold + self._loss_type = loss_type + self._cls_loss_weight = cls_loss_weight + self._loc_loss_weight = loc_loss_weight + self._max_negatives_per_positive = max_negatives_per_positive + self._min_negatives_per_image = min_negatives_per_image + if self._max_negatives_per_positive is not None: + self._max_negatives_per_positive = float(self._max_negatives_per_positive) + self._num_positives_list = None + self._num_negatives_list = None + + def __call__(self, + location_losses, + cls_losses, + decoded_boxlist_list, + match_list=None): + """Computes localization and classification losses after hard mining. + + Args: + location_losses: a float tensor of shape [num_images, num_anchors] + representing anchorwise localization losses. + cls_losses: a float tensor of shape [num_images, num_anchors] + representing anchorwise classification losses. + decoded_boxlist_list: a list of decoded BoxList representing location + predictions for each image. + match_list: an optional list of matcher.Match objects encoding the match + between anchors and groundtruth boxes for each image of the batch, + with rows of the Match objects corresponding to groundtruth boxes + and columns corresponding to anchors. Match objects in match_list are + used to reference which anchors are positive, negative or ignored. If + self._max_negatives_per_positive exists, these are then used to enforce + a prespecified negative to positive ratio. + + Returns: + mined_location_loss: a float scalar with sum of localization losses from + selected hard examples. + mined_cls_loss: a float scalar with sum of classification losses from + selected hard examples. + Raises: + ValueError: if location_losses, cls_losses and decoded_boxlist_list do + not have compatible shapes (i.e., they must correspond to the same + number of images). + ValueError: if match_list is specified but its length does not match + len(decoded_boxlist_list). + """ + mined_location_losses = [] + mined_cls_losses = [] + location_losses = tf.unstack(location_losses) + cls_losses = tf.unstack(cls_losses) + num_images = len(decoded_boxlist_list) + if not match_list: + match_list = num_images * [None] + if not len(location_losses) == len(decoded_boxlist_list) == len(cls_losses): + raise ValueError('location_losses, cls_losses and decoded_boxlist_list ' + 'do not have compatible shapes.') + if not isinstance(match_list, list): + raise ValueError('match_list must be a list.') + if len(match_list) != len(decoded_boxlist_list): + raise ValueError('match_list must either be None or have ' + 'length=len(decoded_boxlist_list).') + num_positives_list = [] + num_negatives_list = [] + for ind, detection_boxlist in enumerate(decoded_boxlist_list): + box_locations = detection_boxlist.get() + match = match_list[ind] + image_losses = cls_losses[ind] + if self._loss_type == 'loc': + image_losses = location_losses[ind] + elif self._loss_type == 'both': + image_losses *= self._cls_loss_weight + image_losses += location_losses[ind] * self._loc_loss_weight + if self._num_hard_examples is not None: + num_hard_examples = self._num_hard_examples + else: + num_hard_examples = detection_boxlist.num_boxes() + selected_indices = tf.image.non_max_suppression( + box_locations, image_losses, num_hard_examples, self._iou_threshold) + if self._max_negatives_per_positive is not None and match: + (selected_indices, num_positives, + num_negatives) = self._subsample_selection_to_desired_neg_pos_ratio( + selected_indices, match, self._max_negatives_per_positive, + self._min_negatives_per_image) + num_positives_list.append(num_positives) + num_negatives_list.append(num_negatives) + mined_location_losses.append( + tf.reduce_sum(tf.gather(location_losses[ind], selected_indices))) + mined_cls_losses.append( + tf.reduce_sum(tf.gather(cls_losses[ind], selected_indices))) + location_loss = tf.reduce_sum(tf.stack(mined_location_losses)) + cls_loss = tf.reduce_sum(tf.stack(mined_cls_losses)) + if match and self._max_negatives_per_positive: + self._num_positives_list = num_positives_list + self._num_negatives_list = num_negatives_list + return (location_loss, cls_loss) + + def summarize(self): + """Summarize the number of positives and negatives after mining.""" + if self._num_positives_list and self._num_negatives_list: + avg_num_positives = tf.reduce_mean( + tf.cast(self._num_positives_list, dtype=tf.float32)) + avg_num_negatives = tf.reduce_mean( + tf.cast(self._num_negatives_list, dtype=tf.float32)) + tf.summary.scalar('HardExampleMiner/NumPositives', avg_num_positives) + tf.summary.scalar('HardExampleMiner/NumNegatives', avg_num_negatives) + + def _subsample_selection_to_desired_neg_pos_ratio(self, + indices, + match, + max_negatives_per_positive, + min_negatives_per_image=0): + """Subsample a collection of selected indices to a desired neg:pos ratio. + + This function takes a subset of M indices (indexing into a large anchor + collection of N anchors where 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/losses_test.py b/workspace/virtuallab/object_detection/core/losses_test.py new file mode 100644 index 0000000..40aa18d --- /dev/null +++ b/workspace/virtuallab/object_detection/core/losses_test.py @@ -0,0 +1,1451 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for google3.research.vale.object_detection.losses.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math + +import numpy as np +from six.moves import zip +import tensorflow.compat.v1 as tf + +from object_detection.core import box_list +from object_detection.core import losses +from object_detection.core import matcher +from object_detection.utils import test_case + + +class WeightedL2LocalizationLossTest(test_case.TestCase): + + def testReturnsCorrectWeightedLoss(self): + batch_size = 3 + num_anchors = 10 + code_size = 4 + def graph_fn(): + prediction_tensor = tf.ones([batch_size, num_anchors, code_size]) + target_tensor = tf.zeros([batch_size, num_anchors, code_size]) + weights = tf.constant([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]], tf.float32) + loss_op = losses.WeightedL2LocalizationLoss() + loss = tf.reduce_sum(loss_op(prediction_tensor, target_tensor, + weights=weights)) + return loss + expected_loss = (3 * 5 * 4) / 2.0 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, expected_loss) + + def testReturnsCorrectAnchorwiseLoss(self): + batch_size = 3 + num_anchors = 16 + code_size = 4 + def graph_fn(): + prediction_tensor = tf.ones([batch_size, num_anchors, code_size]) + target_tensor = tf.zeros([batch_size, num_anchors, code_size]) + weights = tf.ones([batch_size, num_anchors]) + loss_op = losses.WeightedL2LocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + return loss + expected_loss = np.ones((batch_size, num_anchors)) * 2 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, expected_loss) + + def testReturnsCorrectNanLoss(self): + batch_size = 3 + num_anchors = 10 + code_size = 4 + def graph_fn(): + prediction_tensor = tf.ones([batch_size, num_anchors, code_size]) + target_tensor = tf.concat([ + tf.zeros([batch_size, num_anchors, code_size / 2]), + tf.ones([batch_size, num_anchors, code_size / 2]) * np.nan + ], axis=2) + weights = tf.ones([batch_size, num_anchors]) + loss_op = losses.WeightedL2LocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights, + ignore_nan_targets=True) + loss = tf.reduce_sum(loss) + return loss + expected_loss = (3 * 5 * 4) / 2.0 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, expected_loss) + + def testReturnsCorrectWeightedLossWithLossesMask(self): + batch_size = 4 + num_anchors = 10 + code_size = 4 + def graph_fn(): + prediction_tensor = tf.ones([batch_size, num_anchors, code_size]) + target_tensor = tf.zeros([batch_size, num_anchors, code_size]) + weights = tf.constant([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1, 1, 0, 0], + [1, 1, 1, 1, 1, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 0, 0, 0, 0, 0]], tf.float32) + losses_mask = tf.constant([True, False, True, True], tf.bool) + loss_op = losses.WeightedL2LocalizationLoss() + loss = tf.reduce_sum(loss_op(prediction_tensor, target_tensor, + weights=weights, losses_mask=losses_mask)) + return loss + expected_loss = (3 * 5 * 4) / 2.0 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, expected_loss) + + +class WeightedSmoothL1LocalizationLossTest(test_case.TestCase): + + def testReturnsCorrectLoss(self): + batch_size = 2 + num_anchors = 3 + code_size = 4 + def graph_fn(): + prediction_tensor = tf.constant([[[2.5, 0, .4, 0], + [0, 0, 0, 0], + [0, 2.5, 0, .4]], + [[3.5, 0, 0, 0], + [0, .4, 0, .9], + [0, 0, 1.5, 0]]], tf.float32) + target_tensor = tf.zeros([batch_size, num_anchors, code_size]) + weights = tf.constant([[2, 1, 1], + [0, 3, 0]], tf.float32) + loss_op = losses.WeightedSmoothL1LocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + exp_loss = 7.695 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossWithLossesMask(self): + batch_size = 3 + num_anchors = 3 + code_size = 4 + def graph_fn(): + prediction_tensor = tf.constant([[[2.5, 0, .4, 0], + [0, 0, 0, 0], + [0, 2.5, 0, .4]], + [[3.5, 0, 0, 0], + [0, .4, 0, .9], + [0, 0, 1.5, 0]], + [[3.5, 7., 0, 0], + [0, .4, 0, .9], + [2.2, 2.2, 1.5, 0]]], tf.float32) + target_tensor = tf.zeros([batch_size, num_anchors, code_size]) + weights = tf.constant([[2, 1, 1], + [0, 3, 0], + [4, 3, 0]], tf.float32) + losses_mask = tf.constant([True, True, False], tf.bool) + loss_op = losses.WeightedSmoothL1LocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights, + losses_mask=losses_mask) + loss = tf.reduce_sum(loss) + return loss + exp_loss = 7.695 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + +class WeightedIOULocalizationLossTest(test_case.TestCase): + + def testReturnsCorrectLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [0, 0, .5, .25]]]) + target_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [50, 50, 500.5, 100.25]]]) + weights = [[1.0, .5, 2.0]] + loss_op = losses.WeightedIOULocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + exp_loss = 2.0 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossWithNoLabels(self): + def graph_fn(): + prediction_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [0, 0, .5, .25]]]) + target_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [50, 50, 500.5, 100.25]]]) + weights = [[1.0, .5, 2.0]] + losses_mask = tf.constant([False], tf.bool) + loss_op = losses.WeightedIOULocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights, + losses_mask=losses_mask) + loss = tf.reduce_sum(loss) + return loss + exp_loss = 0.0 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + +class WeightedGIOULocalizationLossTest(test_case.TestCase): + + def testReturnsCorrectLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [0, 0, 0, 0]]]) + target_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [5, 5, 10, 10]]]) + weights = [[1.0, .5, 2.0]] + loss_op = losses.WeightedGIOULocalizationLoss() + loss = loss_op(prediction_tensor, + target_tensor, + weights=weights) + loss = tf.reduce_sum(loss) + return loss + exp_loss = 3.5 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossWithNoLabels(self): + def graph_fn(): + prediction_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [0, 0, .5, .25]]]) + target_tensor = tf.constant([[[1.5, 0, 2.4, 1], + [0, 0, 1, 1], + [50, 50, 500.5, 100.25]]]) + weights = [[1.0, .5, 2.0]] + losses_mask = tf.constant([False], tf.bool) + loss_op = losses.WeightedGIOULocalizationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights, + losses_mask=losses_mask) + loss = tf.reduce_sum(loss) + return loss + exp_loss = 0.0 + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + +class WeightedSigmoidClassificationLossTest(test_case.TestCase): + + def testReturnsCorrectLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, 0, -100], + [-100, -100, 100]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + loss_op = losses.WeightedSigmoidClassificationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + + exp_loss = -2 * math.log(.5) + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectAnchorWiseLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, 0, -100], + [-100, -100, 100]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + loss_op = losses.WeightedSigmoidClassificationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss, axis=2) + return loss + + exp_loss = np.matrix([[0, 0, -math.log(.5), 0], + [-math.log(.5), 0, 0, 0]]) + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossWithClassIndices(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100, 100], + [100, -100, -100, -100], + [100, 0, -100, 100], + [-100, -100, 100, -100]], + [[-100, 0, 100, 100], + [-100, 100, -100, 100], + [100, 100, 100, 100], + [0, 0, -1, 100]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0, 0], + [1, 0, 0, 1], + [1, 0, 0, 0], + [0, 0, 1, 1]], + [[0, 0, 1, 0], + [0, 1, 0, 0], + [1, 1, 1, 0], + [1, 0, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]], + [[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [0, 0, 0, 0]]], tf.float32) + # Ignores the last class. + class_indices = tf.constant([0, 1, 2], tf.int32) + loss_op = losses.WeightedSigmoidClassificationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights, + class_indices=class_indices) + loss = tf.reduce_sum(loss, axis=2) + return loss + + exp_loss = np.matrix([[0, 0, -math.log(.5), 0], + [-math.log(.5), 0, 0, 0]]) + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossWithLossesMask(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, 0, -100], + [-100, -100, 100]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -100]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]], + [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]]], tf.float32) + losses_mask = tf.constant([True, True, False], tf.bool) + + loss_op = losses.WeightedSigmoidClassificationLoss() + loss_per_anchor = loss_op(prediction_tensor, target_tensor, + weights=weights, + losses_mask=losses_mask) + loss = tf.reduce_sum(loss_per_anchor) + return loss + + exp_loss = -2 * math.log(.5) + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + +def _logit(probability): + return math.log(probability / (1. - probability)) + + +class SigmoidFocalClassificationLossTest(test_case.TestCase): + + def testEasyExamplesProduceSmallLossComparedToSigmoidXEntropy(self): + def graph_fn(): + prediction_tensor = tf.constant([[[_logit(0.97)], + [_logit(0.91)], + [_logit(0.73)], + [_logit(0.27)], + [_logit(0.09)], + [_logit(0.03)]]], tf.float32) + target_tensor = tf.constant([[[1], + [1], + [1], + [0], + [0], + [0]]], tf.float32) + weights = tf.constant([[[1], [1], [1], [1], [1], [1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(gamma=2.0, + alpha=None) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights), axis=2) + sigmoid_loss = tf.reduce_sum(sigmoid_loss_op(prediction_tensor, + target_tensor, + weights=weights), axis=2) + return sigmoid_loss, focal_loss + + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + order_of_ratio = np.power(10, + np.floor(np.log10(sigmoid_loss / focal_loss))) + self.assertAllClose(order_of_ratio, [[1000, 100, 10, 10, 100, 1000]]) + + def testHardExamplesProduceLossComparableToSigmoidXEntropy(self): + def graph_fn(): + prediction_tensor = tf.constant([[[_logit(0.55)], + [_logit(0.52)], + [_logit(0.50)], + [_logit(0.48)], + [_logit(0.45)]]], tf.float32) + target_tensor = tf.constant([[[1], + [1], + [1], + [0], + [0]]], tf.float32) + weights = tf.constant([[[1], [1], [1], [1], [1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(gamma=2.0, + alpha=None) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights), axis=2) + sigmoid_loss = tf.reduce_sum(sigmoid_loss_op(prediction_tensor, + target_tensor, + weights=weights), axis=2) + return sigmoid_loss, focal_loss + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + order_of_ratio = np.power(10, + np.floor(np.log10(sigmoid_loss / focal_loss))) + self.assertAllClose(order_of_ratio, [[1., 1., 1., 1., 1.]]) + + def testNonAnchorWiseOutputComparableToSigmoidXEntropy(self): + def graph_fn(): + prediction_tensor = tf.constant([[[_logit(0.55)], + [_logit(0.52)], + [_logit(0.50)], + [_logit(0.48)], + [_logit(0.45)]]], tf.float32) + target_tensor = tf.constant([[[1], + [1], + [1], + [0], + [0]]], tf.float32) + weights = tf.constant([[[1], [1], [1], [1], [1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(gamma=2.0, + alpha=None) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights)) + sigmoid_loss = tf.reduce_sum(sigmoid_loss_op(prediction_tensor, + target_tensor, + weights=weights)) + return sigmoid_loss, focal_loss + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + order_of_ratio = np.power(10, + np.floor(np.log10(sigmoid_loss / focal_loss))) + self.assertAlmostEqual(order_of_ratio, 1.) + + def testIgnoreNegativeExampleLossViaAlphaMultiplier(self): + def graph_fn(): + prediction_tensor = tf.constant([[[_logit(0.55)], + [_logit(0.52)], + [_logit(0.50)], + [_logit(0.48)], + [_logit(0.45)]]], tf.float32) + target_tensor = tf.constant([[[1], + [1], + [1], + [0], + [0]]], tf.float32) + weights = tf.constant([[[1], [1], [1], [1], [1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(gamma=2.0, + alpha=1.0) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights), axis=2) + sigmoid_loss = tf.reduce_sum(sigmoid_loss_op(prediction_tensor, + target_tensor, + weights=weights), axis=2) + return sigmoid_loss, focal_loss + + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + self.assertAllClose(focal_loss[0][3:], [0., 0.]) + order_of_ratio = np.power(10, + np.floor(np.log10(sigmoid_loss[0][:3] / + focal_loss[0][:3]))) + self.assertAllClose(order_of_ratio, [1., 1., 1.]) + + def testIgnorePositiveExampleLossViaAlphaMultiplier(self): + def graph_fn(): + prediction_tensor = tf.constant([[[_logit(0.55)], + [_logit(0.52)], + [_logit(0.50)], + [_logit(0.48)], + [_logit(0.45)]]], tf.float32) + target_tensor = tf.constant([[[1], + [1], + [1], + [0], + [0]]], tf.float32) + weights = tf.constant([[[1], [1], [1], [1], [1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(gamma=2.0, + alpha=0.0) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights), axis=2) + sigmoid_loss = tf.reduce_sum(sigmoid_loss_op(prediction_tensor, + target_tensor, + weights=weights), axis=2) + return sigmoid_loss, focal_loss + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + self.assertAllClose(focal_loss[0][:3], [0., 0., 0.]) + order_of_ratio = np.power(10, + np.floor(np.log10(sigmoid_loss[0][3:] / + focal_loss[0][3:]))) + self.assertAllClose(order_of_ratio, [1., 1.]) + + def testSimilarToSigmoidXEntropyWithHalfAlphaAndZeroGammaUpToAScale(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, 0, -100], + [-100, -100, 100]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(alpha=0.5, + gamma=0.0) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = focal_loss_op(prediction_tensor, target_tensor, + weights=weights) + sigmoid_loss = sigmoid_loss_op(prediction_tensor, target_tensor, + weights=weights) + return sigmoid_loss, focal_loss + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + self.assertAllClose(sigmoid_loss, focal_loss * 2) + + def testSameAsSigmoidXEntropyWithNoAlphaAndZeroGamma(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, 0, -100], + [-100, -100, 100]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(alpha=None, + gamma=0.0) + sigmoid_loss_op = losses.WeightedSigmoidClassificationLoss() + focal_loss = focal_loss_op(prediction_tensor, target_tensor, + weights=weights) + sigmoid_loss = sigmoid_loss_op(prediction_tensor, target_tensor, + weights=weights) + return sigmoid_loss, focal_loss + sigmoid_loss, focal_loss = self.execute(graph_fn, []) + self.assertAllClose(sigmoid_loss, focal_loss) + + def testExpectedLossWithAlphaOneAndZeroGamma(self): + def graph_fn(): + # All zeros correspond to 0.5 probability. + prediction_tensor = tf.constant([[[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]], + [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 0, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(alpha=1.0, + gamma=0.0) + + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights)) + return focal_loss + focal_loss = self.execute(graph_fn, []) + self.assertAllClose( + (-math.log(.5) * # x-entropy per class per anchor + 1.0 * # alpha + 8), # positives from 8 anchors + focal_loss) + + def testExpectedLossWithAlpha75AndZeroGamma(self): + def graph_fn(): + # All zeros correspond to 0.5 probability. + prediction_tensor = tf.constant([[[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]], + [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 0, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]]], tf.float32) + focal_loss_op = losses.SigmoidFocalClassificationLoss(alpha=0.75, + gamma=0.0) + + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights)) + return focal_loss + focal_loss = self.execute(graph_fn, []) + self.assertAllClose( + (-math.log(.5) * # x-entropy per class per anchor. + ((0.75 * # alpha for positives. + 8) + # positives from 8 anchors. + (0.25 * # alpha for negatives. + 8 * 2))), # negatives from 8 anchors for two classes. + focal_loss) + + def testExpectedLossWithLossesMask(self): + def graph_fn(): + # All zeros correspond to 0.5 probability. + prediction_tensor = tf.constant([[[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]], + [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]], + [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 0, 0], + [1, 0, 0]], + [[1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]]], tf.float32) + losses_mask = tf.constant([True, True, False], tf.bool) + focal_loss_op = losses.SigmoidFocalClassificationLoss(alpha=0.75, + gamma=0.0) + + focal_loss = tf.reduce_sum(focal_loss_op(prediction_tensor, target_tensor, + weights=weights, + losses_mask=losses_mask)) + return focal_loss + focal_loss = self.execute(graph_fn, []) + self.assertAllClose( + (-math.log(.5) * # x-entropy per class per anchor. + ((0.75 * # alpha for positives. + 8) + # positives from 8 anchors. + (0.25 * # alpha for negatives. + 8 * 2))), # negatives from 8 anchors for two classes. + focal_loss) + + +class WeightedSoftmaxClassificationLossTest(test_case.TestCase): + + def testReturnsCorrectLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [0, 1, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [0.5, 0.5, 0.5], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + loss_op = losses.WeightedSoftmaxClassificationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = - 1.5 * math.log(.5) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectAnchorWiseLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [0, 1, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [0.5, 0.5, 0.5], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + loss_op = losses.WeightedSoftmaxClassificationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = np.matrix([[0, 0, - 0.5 * math.log(.5), 0], + [-math.log(.5), 0, 0, 0]]) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectAnchorWiseLossWithHighLogitScaleSetting(self): + """At very high logit_scale, all predictions will be ~0.33.""" + def graph_fn(): + # TODO(yonib): Also test logit_scale with anchorwise=False. + logit_scale = 10e16 + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [0, 1, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]]], tf.float32) + loss_op = losses.WeightedSoftmaxClassificationLoss( + logit_scale=logit_scale) + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + return loss + uniform_distribution_loss = - math.log(.33333333333) + exp_loss = np.matrix([[uniform_distribution_loss] * 4, + [uniform_distribution_loss] * 4]) + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossWithLossesMask(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [0, 1, 0], + [1, 0, 0]], + [[1, 0, 0], + [1, 0, 0], + [1, 0, 0], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [0.5, 0.5, 0.5], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]]], tf.float32) + losses_mask = tf.constant([True, True, False], tf.bool) + loss_op = losses.WeightedSoftmaxClassificationLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights, + losses_mask=losses_mask) + loss = tf.reduce_sum(loss) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = - 1.5 * math.log(.5) + self.assertAllClose(loss_output, exp_loss) + + +class WeightedSoftmaxClassificationAgainstLogitsLossTest(test_case.TestCase): + + def testReturnsCorrectLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + + target_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, -100, -100], + [-100, -100, 100]], + [[-100, -100, 100], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + weights = tf.constant([[1, 1, .5, 1], + [1, 1, 1, 1]], tf.float32) + weights_shape = tf.shape(weights) + weights_multiple = tf.concat( + [tf.ones_like(weights_shape), tf.constant([3])], + axis=0) + weights = tf.tile(tf.expand_dims(weights, 2), weights_multiple) + loss_op = losses.WeightedSoftmaxClassificationAgainstLogitsLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = - 1.5 * math.log(.5) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectAnchorWiseLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + target_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, -100, -100], + [-100, -100, 100]], + [[-100, -100, 100], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + weights = tf.constant([[1, 1, .5, 1], + [1, 1, 1, 0]], tf.float32) + weights_shape = tf.shape(weights) + weights_multiple = tf.concat( + [tf.ones_like(weights_shape), tf.constant([3])], + axis=0) + weights = tf.tile(tf.expand_dims(weights, 2), weights_multiple) + loss_op = losses.WeightedSoftmaxClassificationAgainstLogitsLoss() + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = np.matrix([[0, 0, - 0.5 * math.log(.5), 0], + [-math.log(.5), 0, 0, 0]]) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectAnchorWiseLossWithLogitScaleSetting(self): + def graph_fn(): + logit_scale = 100. + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + target_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [0, 0, -100], + [-100, -100, 100]], + [[-100, 0, 0], + [-100, 100, -100], + [-100, 100, -100], + [100, -100, -100]]], tf.float32) + weights = tf.constant([[1, 1, .5, 1], + [1, 1, 1, 0]], tf.float32) + weights_shape = tf.shape(weights) + weights_multiple = tf.concat( + [tf.ones_like(weights_shape), tf.constant([3])], + axis=0) + weights = tf.tile(tf.expand_dims(weights, 2), weights_multiple) + loss_op = losses.WeightedSoftmaxClassificationAgainstLogitsLoss( + logit_scale=logit_scale) + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + return loss + + # find softmax of the two prediction types above + softmax_pred1 = [np.exp(-1), np.exp(-1), np.exp(1)] + softmax_pred1 /= sum(softmax_pred1) + softmax_pred2 = [np.exp(0), np.exp(0), np.exp(-1)] + softmax_pred2 /= sum(softmax_pred2) + + # compute the expected cross entropy for perfect matches + exp_entropy1 = sum( + [-x*np.log(x) for x in softmax_pred1]) + exp_entropy2 = sum( + [-x*np.log(x) for x in softmax_pred2]) + + # weighted expected losses + exp_loss = np.matrix( + [[exp_entropy1, exp_entropy1, exp_entropy2*.5, exp_entropy1], + [exp_entropy2, exp_entropy1, exp_entropy1, 0.]]) + loss_output = self.execute(graph_fn, []) + self.assertAllClose(loss_output, exp_loss) + + +class BootstrappedSigmoidClassificationLossTest(test_case.TestCase): + + def testReturnsCorrectLossSoftBootstrapping(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, 0], + [100, -100, -100], + [100, -100, -100], + [-100, -100, 100]], + [[-100, -100, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + alpha = tf.constant(.5, tf.float32) + loss_op = losses.BootstrappedSigmoidClassificationLoss( + alpha, bootstrap_type='soft') + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = -math.log(.5) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectLossHardBootstrapping(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, 0], + [100, -100, -100], + [100, -100, -100], + [-100, -100, 100]], + [[-100, -100, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + alpha = tf.constant(.5, tf.float32) + loss_op = losses.BootstrappedSigmoidClassificationLoss( + alpha, bootstrap_type='hard') + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = -math.log(.5) + self.assertAllClose(loss_output, exp_loss) + + def testReturnsCorrectAnchorWiseLoss(self): + def graph_fn(): + prediction_tensor = tf.constant([[[-100, 100, -100], + [100, -100, -100], + [100, 0, -100], + [-100, -100, 100]], + [[-100, 0, 100], + [-100, 100, -100], + [100, 100, 100], + [0, 0, -1]]], tf.float32) + target_tensor = tf.constant([[[0, 1, 0], + [1, 0, 0], + [1, 0, 0], + [0, 0, 1]], + [[0, 0, 1], + [0, 1, 0], + [1, 1, 1], + [1, 0, 0]]], tf.float32) + weights = tf.constant([[[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]], + [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [0, 0, 0]]], tf.float32) + alpha = tf.constant(.5, tf.float32) + loss_op = losses.BootstrappedSigmoidClassificationLoss( + alpha, bootstrap_type='hard') + loss = loss_op(prediction_tensor, target_tensor, weights=weights) + loss = tf.reduce_sum(loss, axis=2) + return loss + loss_output = self.execute(graph_fn, []) + exp_loss = np.matrix([[0, 0, -math.log(.5), 0], + [-math.log(.5), 0, 0, 0]]) + self.assertAllClose(loss_output, exp_loss) + + +class HardExampleMinerTest(test_case.TestCase): + + def testHardMiningWithSingleLossType(self): + def graph_fn(): + location_losses = tf.constant([[100, 90, 80, 0], + [0, 1, 2, 3]], tf.float32) + cls_losses = tf.constant([[0, 10, 50, 110], + [9, 6, 3, 0]], tf.float32) + box_corners = tf.constant([[0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9]], tf.float32) + decoded_boxlist_list = [] + decoded_boxlist_list.append(box_list.BoxList(box_corners)) + decoded_boxlist_list.append(box_list.BoxList(box_corners)) + # Uses only location loss to select hard examples + loss_op = losses.HardExampleMiner(num_hard_examples=1, + iou_threshold=0.0, + loss_type='loc', + cls_loss_weight=1, + loc_loss_weight=1) + (loc_loss, cls_loss) = loss_op(location_losses, cls_losses, + decoded_boxlist_list) + return loc_loss, cls_loss + loc_loss_output, cls_loss_output = self.execute(graph_fn, []) + exp_loc_loss = 100 + 3 + exp_cls_loss = 0 + 0 + self.assertAllClose(loc_loss_output, exp_loc_loss) + self.assertAllClose(cls_loss_output, exp_cls_loss) + + def testHardMiningWithBothLossType(self): + def graph_fn(): + location_losses = tf.constant([[100, 90, 80, 0], + [0, 1, 2, 3]], tf.float32) + cls_losses = tf.constant([[0, 10, 50, 110], + [9, 6, 3, 0]], tf.float32) + box_corners = tf.constant([[0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9]], tf.float32) + decoded_boxlist_list = [] + decoded_boxlist_list.append(box_list.BoxList(box_corners)) + decoded_boxlist_list.append(box_list.BoxList(box_corners)) + loss_op = losses.HardExampleMiner(num_hard_examples=1, + iou_threshold=0.0, + loss_type='both', + cls_loss_weight=1, + loc_loss_weight=1) + (loc_loss, cls_loss) = loss_op(location_losses, cls_losses, + decoded_boxlist_list) + return loc_loss, cls_loss + loc_loss_output, cls_loss_output = self.execute(graph_fn, []) + exp_loc_loss = 80 + 0 + exp_cls_loss = 50 + 9 + self.assertAllClose(loc_loss_output, exp_loc_loss) + self.assertAllClose(cls_loss_output, exp_cls_loss) + + def testHardMiningNMS(self): + def graph_fn(): + location_losses = tf.constant([[100, 90, 80, 0], + [0, 1, 2, 3]], tf.float32) + cls_losses = tf.constant([[0, 10, 50, 110], + [9, 6, 3, 0]], tf.float32) + box_corners = tf.constant([[0.1, 0.1, 0.9, 0.9], + [0.9, 0.9, 0.99, 0.99], + [0.1, 0.1, 0.9, 0.9], + [0.1, 0.1, 0.9, 0.9]], tf.float32) + decoded_boxlist_list = [] + decoded_boxlist_list.append(box_list.BoxList(box_corners)) + decoded_boxlist_list.append(box_list.BoxList(box_corners)) + loss_op = losses.HardExampleMiner(num_hard_examples=2, + iou_threshold=0.5, + loss_type='cls', + cls_loss_weight=1, + loc_loss_weight=1) + (loc_loss, cls_loss) = loss_op(location_losses, cls_losses, + decoded_boxlist_list) + return loc_loss, cls_loss + loc_loss_output, cls_loss_output = self.execute(graph_fn, []) + exp_loc_loss = 0 + 90 + 0 + 1 + exp_cls_loss = 110 + 10 + 9 + 6 + + self.assertAllClose(loc_loss_output, exp_loc_loss) + self.assertAllClose(cls_loss_output, exp_cls_loss) + + def testEnforceNegativesPerPositiveRatio(self): + location_losses = np.array([[100, 90, 80, 0, 1, 2, + 3, 10, 20, 100, 20, 3]], np.float32) + cls_losses = np.array([[0, 0, 100, 0, 90, 70, + 0, 60, 0, 17, 13, 0]], np.float32) + box_corners = np.array([[0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.5, 0.1], + [0.0, 0.0, 0.6, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.8, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 1.0, 0.1], + [0.0, 0.0, 1.1, 0.1], + [0.0, 0.0, 0.2, 0.1]], np.float32) + match_results = np.array([2, -1, 0, -1, -1, 1, -1, -1, -1, -1, -1, 3], + np.int32) + + max_negatives_per_positive_list = [0.0, 0.5, 1.0, 1.5, 10] + exp_loc_loss_list = [80 + 2, + 80 + 1 + 2, + 80 + 1 + 2 + 10, + 80 + 1 + 2 + 10 + 100, + 80 + 1 + 2 + 10 + 100 + 20] + exp_cls_loss_list = [100 + 70, + 100 + 90 + 70, + 100 + 90 + 70 + 60, + 100 + 90 + 70 + 60 + 17, + 100 + 90 + 70 + 60 + 17 + 13] + + # pylint: disable=cell-var-from-loop + for max_negatives_per_positive, exp_loc_loss, exp_cls_loss in zip( + max_negatives_per_positive_list, exp_loc_loss_list, exp_cls_loss_list): + def graph_fn(): + loss_op = losses.HardExampleMiner( + num_hard_examples=None, iou_threshold=0.9999, loss_type='cls', + cls_loss_weight=1, loc_loss_weight=1, + max_negatives_per_positive=max_negatives_per_positive) + match_list = [matcher.Match(tf.constant(match_results))] + decoded_boxlist_list = [box_list.BoxList(tf.constant(box_corners))] + (loc_loss, cls_loss) = loss_op(tf.constant(location_losses), + tf.constant(cls_losses), + decoded_boxlist_list, match_list) + return loc_loss, cls_loss + loc_loss_output, cls_loss_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(loc_loss_output, exp_loc_loss) + self.assertAllClose(cls_loss_output, exp_cls_loss) + # pylint: enable=cell-var-from-loop + + def testEnforceNegativesPerPositiveRatioWithMinNegativesPerImage(self): + location_losses = np.array([[100, 90, 80, 0, 1, 2, + 3, 10, 20, 100, 20, 3]], np.float32) + cls_losses = np.array([[0, 0, 100, 0, 90, 70, + 0, 60, 0, 17, 13, 0]], np.float32) + box_corners = np.array([[0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.5, 0.1], + [0.0, 0.0, 0.6, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 0.8, 0.1], + [0.0, 0.0, 0.2, 0.1], + [0.0, 0.0, 1.0, 0.1], + [0.0, 0.0, 1.1, 0.1], + [0.0, 0.0, 0.2, 0.1]], np.float32) + match_results = np.array([-1] * 12, np.int32) + + min_negatives_per_image_list = [0, 1, 2, 4, 5, 6] + exp_loc_loss_list = [0, + 80, + 80 + 1, + 80 + 1 + 2 + 10, + 80 + 1 + 2 + 10 + 100, + 80 + 1 + 2 + 10 + 100 + 20] + exp_cls_loss_list = [0, + 100, + 100 + 90, + 100 + 90 + 70 + 60, + 100 + 90 + 70 + 60 + 17, + 100 + 90 + 70 + 60 + 17 + 13] + + # pylint: disable=cell-var-from-loop + for min_negatives_per_image, exp_loc_loss, exp_cls_loss in zip( + min_negatives_per_image_list, exp_loc_loss_list, exp_cls_loss_list): + def graph_fn(): + loss_op = losses.HardExampleMiner( + num_hard_examples=None, iou_threshold=0.9999, loss_type='cls', + cls_loss_weight=1, loc_loss_weight=1, + max_negatives_per_positive=3, + min_negatives_per_image=min_negatives_per_image) + match_list = [matcher.Match(tf.constant(match_results))] + decoded_boxlist_list = [box_list.BoxList(tf.constant(box_corners))] + (loc_loss, cls_loss) = loss_op(location_losses, cls_losses, + decoded_boxlist_list, match_list) + return loc_loss, cls_loss + loc_loss_output, cls_loss_output = self.execute_cpu(graph_fn, []) + self.assertAllClose(loc_loss_output, exp_loc_loss) + self.assertAllClose(cls_loss_output, exp_cls_loss) + # pylint: enable=cell-var-from-loop + + +LOG_2 = np.log(2) +LOG_3 = np.log(3) + + +class PenaltyReducedLogisticFocalLossTest(test_case.TestCase): + """Testing loss function from Equation (1) in [1]. + + [1]: https://arxiv.org/abs/1904.07850 + """ + + def setUp(self): + super(PenaltyReducedLogisticFocalLossTest, self).setUp() + self._prediction = np.array([ + # First batch + [[1 / 2, 1 / 4, 3 / 4], + [3 / 4, 1 / 3, 1 / 3]], + # Second Batch + [[0.0, 1.0, 1 / 2], + [3 / 4, 2 / 3, 1 / 3]]], np.float32) + self._prediction = np.log(self._prediction/(1 - self._prediction)) + + self._target = np.array([ + # First batch + [[1.0, 0.91, 1.0], + [0.36, 0.84, 1.0]], + # Second Batch + [[0.01, 1.0, 0.75], + [0.96, 1.0, 1.0]]], np.float32) + + def test_returns_correct_loss(self): + def graph_fn(prediction, target): + weights = tf.constant([ + [[1.0], [1.0]], + [[1.0], [1.0]], + ]) + loss = losses.PenaltyReducedLogisticFocalLoss(alpha=2.0, beta=0.5) + computed_value = loss._compute_loss(prediction, target, + weights) + return computed_value + computed_value = self.execute(graph_fn, [self._prediction, self._target]) + expected_value = np.array([ + # First batch + [[1 / 4 * LOG_2, + 0.3 * 0.0625 * (2 * LOG_2 - LOG_3), + 1 / 16 * (2 * LOG_2 - LOG_3)], + [0.8 * 9 / 16 * 2 * LOG_2, + 0.4 * 1 / 9 * (LOG_3 - LOG_2), + 4 / 9 * LOG_3]], + # Second Batch + [[0.0, + 0.0, + 1 / 2 * 1 / 4 * LOG_2], + [0.2 * 9 / 16 * 2 * LOG_2, + 1 / 9 * (LOG_3 - LOG_2), + 4 / 9 * LOG_3]]]) + self.assertAllClose(computed_value, expected_value, rtol=1e-3, atol=1e-3) + + def test_returns_correct_loss_weighted(self): + def graph_fn(prediction, target): + weights = tf.constant([ + [[1.0, 0.0, 1.0], [0.0, 0.0, 1.0]], + [[1.0, 1.0, 1.0], [0.0, 0.0, 0.0]], + ]) + + loss = losses.PenaltyReducedLogisticFocalLoss(alpha=2.0, beta=0.5) + + computed_value = loss._compute_loss(prediction, target, + weights) + return computed_value + computed_value = self.execute(graph_fn, [self._prediction, self._target]) + expected_value = np.array([ + # First batch + [[1 / 4 * LOG_2, + 0.0, + 1 / 16 * (2 * LOG_2 - LOG_3)], + [0.0, + 0.0, + 4 / 9 * LOG_3]], + # Second Batch + [[0.0, + 0.0, + 1 / 2 * 1 / 4 * LOG_2], + [0.0, + 0.0, + 0.0]]]) + + self.assertAllClose(computed_value, expected_value, rtol=1e-3, atol=1e-3) + + +class L1LocalizationLossTest(test_case.TestCase): + + def test_returns_correct_loss(self): + def graph_fn(): + loss = losses.L1LocalizationLoss() + pred = [[0.1, 0.2], [0.7, 0.5]] + target = [[0.9, 1.0], [0.1, 0.4]] + + weights = [[1.0, 0.0], [1.0, 1.0]] + return loss._compute_loss(pred, target, weights) + computed_value = self.execute(graph_fn, []) + self.assertAllClose(computed_value, [[0.8, 0.0], [0.6, 0.1]], rtol=1e-6) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/matcher.py b/workspace/virtuallab/object_detection/core/matcher.py new file mode 100644 index 0000000..6a09ffa --- /dev/null +++ b/workspace/virtuallab/object_detection/core/matcher.py @@ -0,0 +1,270 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Matcher interface and Match class. + +This module defines the Matcher interface and the Match object. The job of the +matcher is to match row and column indices based on the similarity matrix and +other optional parameters. Each column is matched to at most one row. There +are three possibilities for the matching: + +1) match: A column matches a row. +2) no_match: A column does not match any row. +3) ignore: A column that is neither 'match' nor no_match. + +The ignore case is regularly encountered in object detection: when an anchor has +a relatively small overlap with a ground-truth box, one neither wants to +consider this box a positive example (match) nor a negative example (no match). + +The Match class is used to store the match results and it provides simple apis +to query the results. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import abc +import six +import tensorflow.compat.v1 as tf + +from object_detection.utils import ops + + +class Match(object): + """Class to store results from the matcher. + + This class is used to store the results from the matcher. It provides + convenient methods to query the matching results. + """ + + def __init__(self, match_results, use_matmul_gather=False): + """Constructs a Match object. + + Args: + match_results: Integer tensor of shape [N] with (1) match_results[i]>=0, + meaning that column i is matched with row match_results[i]. + (2) match_results[i]=-1, meaning that column i is not matched. + (3) match_results[i]=-2, meaning that column i is ignored. + use_matmul_gather: Use matrix multiplication based gather instead of + standard tf.gather. (Default: False). + + Raises: + ValueError: if match_results does not have rank 1 or is not an + integer int32 scalar tensor + """ + if match_results.shape.ndims != 1: + raise ValueError('match_results should have rank 1') + if match_results.dtype != tf.int32: + raise ValueError('match_results should be an int32 or int64 scalar ' + 'tensor') + self._match_results = match_results + self._gather_op = tf.gather + if use_matmul_gather: + self._gather_op = ops.matmul_gather_on_zeroth_axis + + @property + def match_results(self): + """The accessor for match results. + + Returns: + the tensor which encodes the match results. + """ + return self._match_results + + def matched_column_indices(self): + """Returns column indices that match to some row. + + The indices returned by this op are always sorted in increasing order. + + Returns: + column_indices: int32 tensor of shape [K] with column indices. + """ + return self._reshape_and_cast(tf.where(tf.greater(self._match_results, -1))) + + def matched_column_indicator(self): + """Returns column indices that are matched. + + Returns: + column_indices: int32 tensor of shape [K] with column indices. + """ + return tf.greater_equal(self._match_results, 0) + + def num_matched_columns(self): + """Returns number (int32 scalar tensor) of matched columns.""" + return tf.size(self.matched_column_indices()) + + def unmatched_column_indices(self): + """Returns column indices that do not match any row. + + The indices returned by this op are always sorted in increasing order. + + Returns: + column_indices: int32 tensor of shape [K] with column indices. + """ + return self._reshape_and_cast(tf.where(tf.equal(self._match_results, -1))) + + def unmatched_column_indicator(self): + """Returns column indices that are unmatched. + + Returns: + column_indices: int32 tensor of shape [K] with column indices. + """ + return tf.equal(self._match_results, -1) + + def num_unmatched_columns(self): + """Returns number (int32 scalar tensor) of unmatched columns.""" + return tf.size(self.unmatched_column_indices()) + + def ignored_column_indices(self): + """Returns column indices that are ignored (neither Matched nor Unmatched). + + The indices returned by this op are always sorted in increasing order. + + Returns: + column_indices: int32 tensor of shape [K] with column indices. + """ + return self._reshape_and_cast(tf.where(self.ignored_column_indicator())) + + def ignored_column_indicator(self): + """Returns boolean column indicator where True means the colum is ignored. + + Returns: + column_indicator: boolean vector which is True for all ignored column + indices. + """ + return tf.equal(self._match_results, -2) + + def num_ignored_columns(self): + """Returns number (int32 scalar tensor) of matched columns.""" + return tf.size(self.ignored_column_indices()) + + def unmatched_or_ignored_column_indices(self): + """Returns column indices that are unmatched or ignored. + + The indices returned by this op are always sorted in increasing order. + + Returns: + column_indices: int32 tensor of shape [K] with column indices. + """ + return self._reshape_and_cast(tf.where(tf.greater(0, self._match_results))) + + def matched_row_indices(self): + """Returns row indices that match some column. + + The indices returned by this op are ordered so as to be in correspondence + with the output of matched_column_indicator(). For example if + self.matched_column_indicator() is [0,2], and self.matched_row_indices() is + [7, 3], then we know that column 0 was matched to row 7 and column 2 was + matched to row 3. + + Returns: + row_indices: int32 tensor of shape [K] with row indices. + """ + return self._reshape_and_cast( + self._gather_op(tf.cast(self._match_results, dtype=tf.float32), + self.matched_column_indices())) + + def num_matched_rows(self): + """Returns number (int32 scalar tensor) of matched rows.""" + unique_rows, _ = tf.unique(self.matched_row_indices()) + return tf.size(unique_rows) + + def _reshape_and_cast(self, t): + return tf.cast(tf.reshape(t, [-1]), tf.int32) + + def gather_based_on_match(self, input_tensor, unmatched_value, + ignored_value): + """Gathers elements from `input_tensor` based on match results. + + For columns that are matched to a row, gathered_tensor[col] is set to + input_tensor[match_results[col]]. For columns that are unmatched, + gathered_tensor[col] is set to unmatched_value. Finally, for columns that + are ignored gathered_tensor[col] is set to ignored_value. + + Note that the input_tensor.shape[1:] must match with unmatched_value.shape + and ignored_value.shape + + Args: + input_tensor: Tensor to gather values from. + unmatched_value: Constant tensor value for unmatched columns. + ignored_value: Constant tensor value for ignored columns. + + Returns: + gathered_tensor: A tensor containing values gathered from input_tensor. + The shape of the gathered tensor is [match_results.shape[0]] + + input_tensor.shape[1:]. + """ + input_tensor = tf.concat( + [tf.stack([ignored_value, unmatched_value]), + input_tensor], + axis=0) + gather_indices = tf.maximum(self.match_results + 2, 0) + gathered_tensor = self._gather_op(input_tensor, gather_indices) + return gathered_tensor + + +class Matcher(six.with_metaclass(abc.ABCMeta, object)): + """Abstract base class for matcher. + """ + + def __init__(self, use_matmul_gather=False): + """Constructs a Matcher. + + Args: + use_matmul_gather: Force constructed match objects to use matrix + multiplication based gather instead of standard tf.gather. + (Default: False). + """ + self._use_matmul_gather = use_matmul_gather + + def match(self, similarity_matrix, valid_rows=None, scope=None): + """Computes matches among row and column indices and returns the result. + + Computes matches among the row and column indices based on the similarity + matrix and optional arguments. + + Args: + similarity_matrix: Float tensor of shape [N, M] with pairwise similarity + where higher value means more similar. + valid_rows: A boolean tensor of shape [N] indicating the rows that are + valid for matching. + scope: Op scope name. Defaults to 'Match' if None. + + Returns: + A Match object with the results of matching. + """ + with tf.name_scope(scope, 'Match') as scope: + if valid_rows is None: + valid_rows = tf.ones(tf.shape(similarity_matrix)[0], dtype=tf.bool) + return Match(self._match(similarity_matrix, valid_rows), + self._use_matmul_gather) + + @abc.abstractmethod + def _match(self, similarity_matrix, valid_rows): + """Method to be overridden by implementations. + + Args: + similarity_matrix: Float tensor of shape [N, M] with pairwise similarity + where higher value means more similar. + valid_rows: A boolean tensor of shape [N] indicating the rows that are + valid for matching. + Returns: + match_results: Integer tensor of shape [M]: match_results[i]>=0 means + that column i is matched to row match_results[i], match_results[i]=-1 + means that the column is not matched. match_results[i]=-2 means that + the column is ignored (usually this happens when there is a very weak + match which one neither wants as positive nor negative example). + """ + pass diff --git a/workspace/virtuallab/object_detection/core/matcher.pyc b/workspace/virtuallab/object_detection/core/matcher.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb80d237f406f702d383b7bbed8a1cd80f566e6a GIT binary patch literal 11770 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 TY76|YJ_iUqUSF6x@!@{}W{Rg< literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/matcher_test.py b/workspace/virtuallab/object_detection/core/matcher_test.py new file mode 100644 index 0000000..ad64075 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/matcher_test.py @@ -0,0 +1,191 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.matcher.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import matcher +from object_detection.utils import test_case + + +class MatchTest(test_case.TestCase): + + def test_get_correct_matched_columnIndices(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + matched_column_indices = match.matched_column_indices() + return matched_column_indices + expected_column_indices = [0, 1, 3, 5] + matched_column_indices = self.execute(graph_fn, []) + self.assertAllEqual(matched_column_indices, expected_column_indices) + + def test_get_correct_counts(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 1, -2]) + match = matcher.Match(match_results) + num_matched_columns = match.num_matched_columns() + num_unmatched_columns = match.num_unmatched_columns() + num_ignored_columns = match.num_ignored_columns() + num_matched_rows = match.num_matched_rows() + return [num_matched_columns, num_unmatched_columns, num_ignored_columns, + num_matched_rows] + (num_matched_columns_out, num_unmatched_columns_out, + num_ignored_columns_out, + num_matched_rows_out) = self.execute_cpu(graph_fn, []) + exp_num_matched_columns = 4 + exp_num_unmatched_columns = 2 + exp_num_ignored_columns = 1 + exp_num_matched_rows = 3 + self.assertAllEqual(num_matched_columns_out, exp_num_matched_columns) + self.assertAllEqual(num_unmatched_columns_out, exp_num_unmatched_columns) + self.assertAllEqual(num_ignored_columns_out, exp_num_ignored_columns) + self.assertAllEqual(num_matched_rows_out, exp_num_matched_rows) + + def testGetCorrectUnmatchedColumnIndices(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + unmatched_column_indices = match.unmatched_column_indices() + return unmatched_column_indices + unmatched_column_indices = self.execute(graph_fn, []) + expected_column_indices = [2, 4] + self.assertAllEqual(unmatched_column_indices, expected_column_indices) + + def testGetCorrectMatchedRowIndices(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + matched_row_indices = match.matched_row_indices() + return matched_row_indices + matched_row_indices = self.execute(graph_fn, []) + expected_row_indices = [3, 1, 0, 5] + self.assertAllEqual(matched_row_indices, expected_row_indices) + + def test_get_correct_ignored_column_indices(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + ignored_column_indices = match.ignored_column_indices() + return ignored_column_indices + ignored_column_indices = self.execute(graph_fn, []) + expected_column_indices = [6] + self.assertAllEqual(ignored_column_indices, expected_column_indices) + + def test_get_correct_matched_column_indicator(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + matched_column_indicator = match.matched_column_indicator() + return matched_column_indicator + expected_column_indicator = [True, True, False, True, False, True, False] + matched_column_indicator = self.execute(graph_fn, []) + self.assertAllEqual(matched_column_indicator, expected_column_indicator) + + def test_get_correct_unmatched_column_indicator(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + unmatched_column_indicator = match.unmatched_column_indicator() + return unmatched_column_indicator + expected_column_indicator = [False, False, True, False, True, False, False] + unmatched_column_indicator = self.execute(graph_fn, []) + self.assertAllEqual(unmatched_column_indicator, expected_column_indicator) + + def test_get_correct_ignored_column_indicator(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + ignored_column_indicator = match.ignored_column_indicator() + return ignored_column_indicator + expected_column_indicator = [False, False, False, False, False, False, True] + ignored_column_indicator = self.execute(graph_fn, []) + self.assertAllEqual(ignored_column_indicator, expected_column_indicator) + + def test_get_correct_unmatched_ignored_column_indices(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + match = matcher.Match(match_results) + unmatched_ignored_column_indices = (match. + unmatched_or_ignored_column_indices()) + return unmatched_ignored_column_indices + expected_column_indices = [2, 4, 6] + unmatched_ignored_column_indices = self.execute(graph_fn, []) + self.assertAllEqual(unmatched_ignored_column_indices, + expected_column_indices) + + def test_all_columns_accounted_for(self): + # Note: deliberately setting to small number so not always + # all possibilities appear (matched, unmatched, ignored) + def graph_fn(): + match_results = tf.random_uniform( + [num_matches], minval=-2, maxval=5, dtype=tf.int32) + match = matcher.Match(match_results) + matched_column_indices = match.matched_column_indices() + unmatched_column_indices = match.unmatched_column_indices() + ignored_column_indices = match.ignored_column_indices() + return (matched_column_indices, unmatched_column_indices, + ignored_column_indices) + num_matches = 10 + matched, unmatched, ignored = self.execute(graph_fn, []) + all_indices = np.hstack((matched, unmatched, ignored)) + all_indices_sorted = np.sort(all_indices) + self.assertAllEqual(all_indices_sorted, + np.arange(num_matches, dtype=np.int32)) + + def test_scalar_gather_based_on_match(self): + def graph_fn(): + match_results = tf.constant([3, 1, -1, 0, -1, 5, -2]) + input_tensor = tf.constant([0, 1, 2, 3, 4, 5, 6, 7], dtype=tf.float32) + match = matcher.Match(match_results) + gathered_tensor = match.gather_based_on_match(input_tensor, + unmatched_value=100., + ignored_value=200.) + return gathered_tensor + expected_gathered_tensor = [3, 1, 100, 0, 100, 5, 200] + gathered_tensor_out = self.execute(graph_fn, []) + self.assertAllEqual(expected_gathered_tensor, gathered_tensor_out) + + def test_multidimensional_gather_based_on_match(self): + def graph_fn(): + match_results = tf.constant([1, -1, -2]) + input_tensor = tf.constant([[0, 0.5, 0, 0.5], [0, 0, 0.5, 0.5]], + dtype=tf.float32) + match = matcher.Match(match_results) + gathered_tensor = match.gather_based_on_match(input_tensor, + unmatched_value=tf.zeros(4), + ignored_value=tf.zeros(4)) + return gathered_tensor + expected_gathered_tensor = [[0, 0, 0.5, 0.5], [0, 0, 0, 0], [0, 0, 0, 0]] + gathered_tensor_out = self.execute(graph_fn, []) + self.assertAllEqual(expected_gathered_tensor, gathered_tensor_out) + + def test_multidimensional_gather_based_on_match_with_matmul_gather_op(self): + def graph_fn(): + match_results = tf.constant([1, -1, -2]) + input_tensor = tf.constant([[0, 0.5, 0, 0.5], [0, 0, 0.5, 0.5]], + dtype=tf.float32) + match = matcher.Match(match_results, use_matmul_gather=True) + gathered_tensor = match.gather_based_on_match(input_tensor, + unmatched_value=tf.zeros(4), + ignored_value=tf.zeros(4)) + return gathered_tensor + expected_gathered_tensor = [[0, 0, 0.5, 0.5], [0, 0, 0, 0], [0, 0, 0, 0]] + gathered_tensor_out = self.execute(graph_fn, []) + self.assertAllEqual(expected_gathered_tensor, gathered_tensor_out) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/minibatch_sampler.py b/workspace/virtuallab/object_detection/core/minibatch_sampler.py new file mode 100644 index 0000000..9a5b0a7 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/minibatch_sampler.py @@ -0,0 +1,94 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Base minibatch sampler module. + +The job of the minibatch_sampler is to subsample a minibatch based on some +criterion. + +The main function call is: + subsample(indicator, batch_size, **params). +Indicator is a 1d boolean tensor where True denotes which examples can be +sampled. It returns a boolean indicator where True denotes an example has been +sampled.. + +Subclasses should implement the Subsample function and can make use of the +@staticmethod SubsampleIndicator. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from abc import ABCMeta +from abc import abstractmethod + +import six +import tensorflow.compat.v1 as tf + +from object_detection.utils import ops + + +class MinibatchSampler(six.with_metaclass(ABCMeta, object)): + """Abstract base class for subsampling minibatches.""" + + def __init__(self): + """Constructs a minibatch sampler.""" + pass + + @abstractmethod + def subsample(self, indicator, batch_size, **params): + """Returns subsample of entries in indicator. + + Args: + indicator: boolean tensor of shape [N] whose True entries can be sampled. + batch_size: desired batch size. + **params: additional keyword arguments for specific implementations of + the MinibatchSampler. + + Returns: + sample_indicator: boolean tensor of shape [N] whose True entries have been + sampled. If sum(indicator) >= batch_size, sum(is_sampled) = batch_size + """ + pass + + @staticmethod + def subsample_indicator(indicator, num_samples): + """Subsample indicator vector. + + Given a boolean indicator vector with M elements set to `True`, the function + assigns all but `num_samples` of these previously `True` elements to + `False`. If `num_samples` is greater than M, the original indicator vector + is returned. + + Args: + indicator: a 1-dimensional boolean tensor indicating which elements + are allowed to be sampled and which are not. + num_samples: int32 scalar tensor + + Returns: + a boolean tensor with the same shape as input (indicator) tensor + """ + indices = tf.where(indicator) + indices = tf.random_shuffle(indices) + indices = tf.reshape(indices, [-1]) + + num_samples = tf.minimum(tf.size(indices), num_samples) + selected_indices = tf.slice(indices, [0], tf.reshape(num_samples, [1])) + + selected_indicator = ops.indices_to_dense_vector(selected_indices, + tf.shape(indicator)[0]) + + return tf.equal(selected_indicator, 1) diff --git a/workspace/virtuallab/object_detection/core/minibatch_sampler.pyc b/workspace/virtuallab/object_detection/core/minibatch_sampler.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c2fdc12e2528dbec0d67ab35c1aec15a0d45629 GIT binary patch literal 3372 zcmb_eL37+R6qdc4O|os1v_KiA1BerwPS{X7Tmp33W(KCiqz9)LmSG&Nt<5U6t;Ld) zmPt?L%n#tqi5owIGe3nN0N;~jZ`#6vp__?p$CB+q4~ ztuz=?*sOfwc5C3f;!`>FJIKfUf#tQdO;?IKN*C9V2#g(fiYQi=CIwvW||ek z7`Qfb-4r=jl;=`b)}58*XplNG`- z=o7lDEIAzYvC@2u=3a=x;f%Wk^BE4jMUSeEmtd4;oKa5N|9uLAOu_Nth{7`OquJLL zw$@~pj=n>C%Zu^D5$1&Cx;SJp^FuUlK0~uR2iSMPnO%@b7G_sZPh@7(Tv|Mk@7+w- za<{YHGHJYcYUxfO`!bIwG}(J##{O^0BkbFEDivlryS^3Iu%t zd)rwphxfS1b48gE1%E7`p6NR0qCRej9>$;OLS|~FvL$W60h|;_5nR%iNT6N@Uf6BO zj#n4xi$f(69mM57?|UvzrDr)gZVi%jAYo0pl&x)k=Zg#Z*zsRYIP`p*U;OI!5+Fv$ zoe=r#I+`w0J4A1(gm!?gm%`TwL-N?+=r(m64mUD-tl*!#Is*NI7t7d5#LgXIpT({l zv-6nv8@fH}f@jAO6{IlOcQaSQ%ya)Ka)SdmKc@L)9Lrq|^~<04*s~t9tL$Wro#O=) z)`NHG#yjM|n;x=rNJ)=e2GRHc@pp>3L_I$RF?AiF@|8N370Cap?)z!{OxZa<;L__i z6kS5ZH9sO^9PKz1wi48GP2SpebDI3+`QR@DOMaFL|JC)zz|jVMO6C?RTEy1iXUknF&xn_DAFWbz)G5bmtOH4)J#7-9+G zC2>%pf-R<5LQfaDr8lFV*f;U?Xq$l$<4l(eVaKPpoRa&VL)+=C{-)so literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/minibatch_sampler_test.py b/workspace/virtuallab/object_detection/core/minibatch_sampler_test.py new file mode 100644 index 0000000..b3ddadd --- /dev/null +++ b/workspace/virtuallab/object_detection/core/minibatch_sampler_test.py @@ -0,0 +1,71 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for google3.research.vale.object_detection.minibatch_sampler.""" + +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import minibatch_sampler +from object_detection.utils import test_case + + +class MinibatchSamplerTest(test_case.TestCase): + + def test_subsample_indicator_when_more_true_elements_than_num_samples(self): + np_indicator = np.array([True, False, True, False, True, True, False]) + def graph_fn(indicator): + samples = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 3) + return samples + samples_out = self.execute(graph_fn, [np_indicator]) + self.assertTrue(np.sum(samples_out), 3) + self.assertAllEqual(samples_out, + np.logical_and(samples_out, np_indicator)) + + def test_subsample_indicator_when_less_true_elements_than_num_samples(self): + np_indicator = np.array([True, False, True, False, True, True, False]) + def graph_fn(indicator): + samples = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 5) + return samples + samples_out = self.execute(graph_fn, [np_indicator]) + self.assertTrue(np.sum(samples_out), 4) + self.assertAllEqual(samples_out, + np.logical_and(samples_out, np_indicator)) + + def test_subsample_indicator_when_num_samples_is_zero(self): + np_indicator = np.array([True, False, True, False, True, True, False]) + def graph_fn(indicator): + samples_none = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 0) + return samples_none + samples_out = self.execute(graph_fn, [np_indicator]) + self.assertAllEqual( + np.zeros_like(samples_out, dtype=bool), + samples_out) + + def test_subsample_indicator_when_indicator_all_false(self): + indicator_empty = np.zeros([0], dtype=np.bool) + def graph_fn(indicator): + samples_empty = minibatch_sampler.MinibatchSampler.subsample_indicator( + indicator, 4) + return samples_empty + samples_out = self.execute(graph_fn, [indicator_empty]) + self.assertEqual(0, samples_out.size) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/model.py b/workspace/virtuallab/object_detection/core/model.py new file mode 100644 index 0000000..be45152 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/model.py @@ -0,0 +1,550 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Abstract detection model. + +This file defines a generic base class for detection models. Programs that are +designed to work with arbitrary detection models should only depend on this +class. We intend for the functions in this class to follow tensor-in/tensor-out +design, thus all functions have tensors or lists/dictionaries holding tensors as +inputs and outputs. + +Abstractly, detection models predict output tensors given input images +which can be passed to a loss function at training time or passed to a +postprocessing function at eval time. The computation graphs at a high level +consequently look as follows: + +Training time: +inputs (images tensor) -> preprocess -> predict -> loss -> outputs (loss tensor) + +Evaluation time: +inputs (images tensor) -> preprocess -> predict -> postprocess + -> outputs (boxes tensor, scores tensor, classes tensor, num_detections tensor) + +DetectionModels must thus implement four functions (1) preprocess, (2) predict, +(3) postprocess and (4) loss. DetectionModels should make no assumptions about +the input size or aspect ratio --- they are responsible for doing any +resize/reshaping necessary (see docstring for the preprocess function). +Output classes are always integers in the range [0, num_classes). Any mapping +of these integers to semantic labels is to be handled outside of this class. + +Images are resized in the `preprocess` method. All of `preprocess`, `predict`, +and `postprocess` should be reentrant. + +The `preprocess` method runs `image_resizer_fn` that returns resized_images and +`true_image_shapes`. Since `image_resizer_fn` can pad the images with zeros, +true_image_shapes indicate the slices that contain the image without padding. +This is useful for padding images to be a fixed size for batching. + +The `postprocess` method uses the true image shapes to clip predictions that lie +outside of images. + +By default, DetectionModels produce bounding box detections; However, we support +a handful of auxiliary annotations associated with each bounding box, namely, +instance masks and keypoints. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import abc +import six +import tensorflow.compat.v1 as tf + +from object_detection.core import standard_fields as fields + + +# If using a new enough version of TensorFlow, detection models should be a +# tf module or keras model for tracking. +try: + _BaseClass = tf.keras.layers.Layer +except AttributeError: + _BaseClass = object + + +class DetectionModel(six.with_metaclass(abc.ABCMeta, _BaseClass)): + """Abstract base class for detection models. + + Extends tf.Module to guarantee variable tracking. + """ + + def __init__(self, num_classes): + """Constructor. + + Args: + num_classes: number of classes. Note that num_classes *does not* include + background categories that might be implicitly predicted in various + implementations. + """ + self._num_classes = num_classes + self._groundtruth_lists = {} + + super(DetectionModel, self).__init__() + + @property + def num_classes(self): + return self._num_classes + + def groundtruth_lists(self, field): + """Access list of groundtruth tensors. + + Args: + field: a string key, options are + fields.BoxListFields.{boxes,classes,masks,keypoints, + keypoint_visibilities, densepose_*, track_ids, + temporal_offsets, track_match_flags} + fields.InputDataFields.is_annotated. + + Returns: + a list of tensors holding groundtruth information (see also + provide_groundtruth function below), with one entry for each image in the + batch. + Raises: + RuntimeError: if the field has not been provided via provide_groundtruth. + """ + if field not in self._groundtruth_lists: + raise RuntimeError('Groundtruth tensor {} has not been provided'.format( + field)) + return self._groundtruth_lists[field] + + def groundtruth_has_field(self, field): + """Determines whether the groundtruth includes the given field. + + Args: + field: a string key, options are + fields.BoxListFields.{boxes,classes,masks,keypoints, + keypoint_visibilities, densepose_*, track_ids} or + fields.InputDataFields.is_annotated. + + Returns: + True if the groundtruth includes the given field, False otherwise. + """ + return field in self._groundtruth_lists + + @staticmethod + def get_side_inputs(features): + """Get side inputs from input features. + + This placeholder method provides a way for a meta-architecture to specify + how to grab additional side inputs from input features (in addition to the + image itself) and allows models to depend on contextual information. By + default, detection models do not use side information (and thus this method + returns an empty dictionary by default. However it can be overridden if + side inputs are necessary." + + Args: + features: A dictionary of tensors. + + Returns: + An empty dictionary by default. + """ + return {} + + @abc.abstractmethod + def preprocess(self, inputs): + """Input preprocessing. + + To be overridden by implementations. + + This function is responsible for any scaling/shifting of input values that + is necessary prior to running the detector on an input image. + It is also responsible for any resizing, padding that might be necessary + as images are assumed to arrive in arbitrary sizes. While this function + could conceivably be part of the predict method (below), it is often + convenient to keep these separate --- for example, we may want to preprocess + on one device, place onto a queue, and let another device (e.g., the GPU) + handle prediction. + + A few important notes about the preprocess function: + + We assume that this operation does not have any trainable variables nor + does it affect the groundtruth annotations in any way (thus data + augmentation operations such as random cropping should be performed + externally). + + There is no assumption that the batchsize in this function is the same as + the batch size in the predict function. In fact, we recommend calling the + preprocess function prior to calling any batching operations (which should + happen outside of the model) and thus assuming that batch sizes are equal + to 1 in the preprocess function. + + There is also no explicit assumption that the output resolutions + must be fixed across inputs --- this is to support "fully convolutional" + settings in which input images can have different shapes/resolutions. + + Args: + inputs: a [batch, height_in, width_in, channels] float32 tensor + representing a batch of images with values between 0 and 255.0. + + Returns: + preprocessed_inputs: a [batch, height_out, width_out, channels] float32 + tensor representing a batch of images. + true_image_shapes: int32 tensor of shape [batch, 3] where each row is + of the form [height, width, channels] indicating the shapes + of true images in the resized images, as resized images can be padded + with zeros. + """ + pass + + @abc.abstractmethod + def predict(self, preprocessed_inputs, true_image_shapes, **side_inputs): + """Predict prediction tensors from inputs tensor. + + Outputs of this function can be passed to loss or postprocess functions. + + Args: + preprocessed_inputs: a [batch, height, width, channels] float32 tensor + representing a batch of images. + true_image_shapes: int32 tensor of shape [batch, 3] where each row is + of the form [height, width, channels] indicating the shapes + of true images in the resized images, as resized images can be padded + with zeros. + **side_inputs: additional tensors that are required by the network. + + Returns: + prediction_dict: a dictionary holding prediction tensors to be + passed to the Loss or Postprocess functions. + """ + pass + + @abc.abstractmethod + def postprocess(self, prediction_dict, true_image_shapes, **params): + """Convert predicted output tensors to final detections. + + This stage typically performs a few things such as + * Non-Max Suppression to remove overlapping detection boxes. + * Score conversion and background class removal. + + Outputs adhere to the following conventions: + * Classes are integers in [0, num_classes); background classes are removed + and the first non-background class is mapped to 0. If the model produces + class-agnostic detections, then no output is produced for classes. + * Boxes are to be interpreted as being in [y_min, x_min, y_max, x_max] + format and normalized relative to the image window. + * `num_detections` is provided for settings where detections are padded to a + fixed number of boxes. + * We do not specifically assume any kind of probabilistic interpretation + of the scores --- the only important thing is their relative ordering. + Thus implementations of the postprocess function are free to output + logits, probabilities, calibrated probabilities, or anything else. + + Args: + prediction_dict: a dictionary holding prediction tensors. + true_image_shapes: int32 tensor of shape [batch, 3] where each row is + of the form [height, width, channels] indicating the shapes + of true images in the resized images, as resized images can be padded + with zeros. + **params: Additional keyword arguments for specific implementations of + DetectionModel. + + Returns: + detections: a dictionary containing the following fields + detection_boxes: [batch, max_detections, 4] + detection_scores: [batch, max_detections] + detection_classes: [batch, max_detections] + (If a model is producing class-agnostic detections, this field may be + missing) + detection_masks: [batch, max_detections, mask_height, mask_width] + (optional) + detection_keypoints: [batch, max_detections, num_keypoints, 2] + (optional) + detection_keypoint_scores: [batch, max_detections, num_keypoints] + (optional) + detection_surface_coords: [batch, max_detections, mask_height, + mask_width, 2] (optional) + num_detections: [batch] + + In addition to the above fields this stage also outputs the following + raw tensors: + + raw_detection_boxes: [batch, total_detections, 4] tensor containing + all detection boxes from `prediction_dict` in the format + [ymin, xmin, ymax, xmax] and normalized co-ordinates. + raw_detection_scores: [batch, total_detections, + num_classes_with_background] tensor of class score logits for + raw detection boxes. + """ + pass + + @abc.abstractmethod + def loss(self, prediction_dict, true_image_shapes): + """Compute scalar loss tensors with respect to provided groundtruth. + + Calling this function requires that groundtruth tensors have been + provided via the provide_groundtruth function. + + Args: + prediction_dict: a dictionary holding predicted tensors + true_image_shapes: int32 tensor of shape [batch, 3] where each row is + of the form [height, width, channels] indicating the shapes + of true images in the resized images, as resized images can be padded + with zeros. + + Returns: + a dictionary mapping strings (loss names) to scalar tensors representing + loss values. + """ + pass + + def provide_groundtruth( + self, + groundtruth_boxes_list, + groundtruth_classes_list, + groundtruth_masks_list=None, + groundtruth_keypoints_list=None, + groundtruth_keypoint_visibilities_list=None, + groundtruth_dp_num_points_list=None, + groundtruth_dp_part_ids_list=None, + groundtruth_dp_surface_coords_list=None, + groundtruth_track_ids_list=None, + groundtruth_temporal_offsets_list=None, + groundtruth_track_match_flags_list=None, + groundtruth_weights_list=None, + groundtruth_confidences_list=None, + groundtruth_is_crowd_list=None, + groundtruth_group_of_list=None, + groundtruth_area_list=None, + is_annotated_list=None, + groundtruth_labeled_classes=None, + groundtruth_verified_neg_classes=None, + groundtruth_not_exhaustive_classes=None): + """Provide groundtruth tensors. + + Args: + groundtruth_boxes_list: a list of 2-D tf.float32 tensors of shape + [num_boxes, 4] containing coordinates of the groundtruth boxes. + Groundtruth boxes are provided in [y_min, x_min, y_max, x_max] + format and assumed to be normalized and clipped + relative to the image window with y_min <= y_max and x_min <= x_max. + groundtruth_classes_list: a list of 2-D tf.float32 one-hot (or k-hot) + tensors of shape [num_boxes, num_classes] containing the class targets + with the 0th index assumed to map to the first non-background class. + groundtruth_masks_list: a list of 3-D tf.float32 tensors of + shape [num_boxes, height_in, width_in] containing instance + masks with values in {0, 1}. If None, no masks are provided. + Mask resolution `height_in`x`width_in` must agree with the resolution + of the input image tensor provided to the `preprocess` function. + groundtruth_keypoints_list: a list of 3-D tf.float32 tensors of + shape [num_boxes, num_keypoints, 2] containing keypoints. + Keypoints are assumed to be provided in normalized coordinates and + missing keypoints should be encoded as NaN (but it is recommended to use + `groundtruth_keypoint_visibilities_list`). + groundtruth_keypoint_visibilities_list: a list of 3-D tf.bool tensors + of shape [num_boxes, num_keypoints] containing keypoint visibilities. + groundtruth_dp_num_points_list: a list of 1-D tf.int32 tensors of shape + [num_boxes] containing the number of DensePose sampled points. + groundtruth_dp_part_ids_list: a list of 2-D tf.int32 tensors of shape + [num_boxes, max_sampled_points] containing the DensePose part ids + (0-indexed) for each sampled point. Note that there may be padding. + groundtruth_dp_surface_coords_list: a list of 3-D tf.float32 tensors of + shape [num_boxes, max_sampled_points, 4] containing the DensePose + surface coordinates for each sampled point. Note that there may be + padding. + groundtruth_track_ids_list: a list of 1-D tf.int32 tensors of shape + [num_boxes] containing the track IDs of groundtruth objects. + groundtruth_temporal_offsets_list: a list of 2-D tf.float32 tensors + of shape [num_boxes, 2] containing the spatial offsets of objects' + centers compared with the previous frame. + groundtruth_track_match_flags_list: a list of 1-D tf.float32 tensors + of shape [num_boxes] containing 0-1 flags that indicate if an object + has existed in the previous frame. + groundtruth_weights_list: A list of 1-D tf.float32 tensors of shape + [num_boxes] containing weights for groundtruth boxes. + groundtruth_confidences_list: A list of 2-D tf.float32 tensors of shape + [num_boxes, num_classes] containing class confidences for groundtruth + boxes. + groundtruth_is_crowd_list: A list of 1-D tf.bool tensors of shape + [num_boxes] containing is_crowd annotations. + groundtruth_group_of_list: A list of 1-D tf.bool tensors of shape + [num_boxes] containing group_of annotations. + groundtruth_area_list: A list of 1-D tf.float32 tensors of shape + [num_boxes] containing the area (in the original absolute coordinates) + of the annotations. + is_annotated_list: A list of scalar tf.bool tensors indicating whether + images have been labeled or not. + groundtruth_labeled_classes: A list of 1-D tf.float32 tensors of shape + [num_classes], containing label indices encoded as k-hot of the classes + that are exhaustively annotated. + groundtruth_verified_neg_classes: A list of 1-D tf.float32 tensors of + shape [num_classes], containing a K-hot representation of classes + which were verified as not present in the image. + groundtruth_not_exhaustive_classes: A list of 1-D tf.float32 tensors of + shape [num_classes], containing a K-hot representation of classes + which don't have all of their instances marked exhaustively. + """ + self._groundtruth_lists[fields.BoxListFields.boxes] = groundtruth_boxes_list + self._groundtruth_lists[ + fields.BoxListFields.classes] = groundtruth_classes_list + if groundtruth_weights_list: + self._groundtruth_lists[fields.BoxListFields. + weights] = groundtruth_weights_list + if groundtruth_confidences_list: + self._groundtruth_lists[fields.BoxListFields. + confidences] = groundtruth_confidences_list + if groundtruth_masks_list: + self._groundtruth_lists[ + fields.BoxListFields.masks] = groundtruth_masks_list + if groundtruth_keypoints_list: + self._groundtruth_lists[ + fields.BoxListFields.keypoints] = groundtruth_keypoints_list + if groundtruth_keypoint_visibilities_list: + self._groundtruth_lists[ + fields.BoxListFields.keypoint_visibilities] = ( + groundtruth_keypoint_visibilities_list) + if groundtruth_dp_num_points_list: + self._groundtruth_lists[ + fields.BoxListFields.densepose_num_points] = ( + groundtruth_dp_num_points_list) + if groundtruth_dp_part_ids_list: + self._groundtruth_lists[ + fields.BoxListFields.densepose_part_ids] = ( + groundtruth_dp_part_ids_list) + if groundtruth_dp_surface_coords_list: + self._groundtruth_lists[ + fields.BoxListFields.densepose_surface_coords] = ( + groundtruth_dp_surface_coords_list) + if groundtruth_track_ids_list: + self._groundtruth_lists[ + fields.BoxListFields.track_ids] = groundtruth_track_ids_list + if groundtruth_temporal_offsets_list: + self._groundtruth_lists[ + fields.BoxListFields.temporal_offsets] = ( + groundtruth_temporal_offsets_list) + if groundtruth_track_match_flags_list: + self._groundtruth_lists[ + fields.BoxListFields.track_match_flags] = ( + groundtruth_track_match_flags_list) + if groundtruth_is_crowd_list: + self._groundtruth_lists[ + fields.BoxListFields.is_crowd] = groundtruth_is_crowd_list + if groundtruth_group_of_list: + self._groundtruth_lists[ + fields.BoxListFields.group_of] = groundtruth_group_of_list + if groundtruth_area_list: + self._groundtruth_lists[ + fields.InputDataFields.groundtruth_area] = groundtruth_area_list + if is_annotated_list: + self._groundtruth_lists[ + fields.InputDataFields.is_annotated] = is_annotated_list + if groundtruth_labeled_classes: + self._groundtruth_lists[ + fields.InputDataFields + .groundtruth_labeled_classes] = groundtruth_labeled_classes + if groundtruth_verified_neg_classes: + self._groundtruth_lists[ + fields.InputDataFields + .groundtruth_verified_neg_classes] = groundtruth_verified_neg_classes + if groundtruth_not_exhaustive_classes: + self._groundtruth_lists[ + fields.InputDataFields + .groundtruth_not_exhaustive_classes] = ( + groundtruth_not_exhaustive_classes) + + @abc.abstractmethod + def regularization_losses(self): + """Returns a list of regularization losses for this model. + + Returns a list of regularization losses for this model that the estimator + needs to use during training/optimization. + + Returns: + A list of regularization loss tensors. + """ + pass + + @abc.abstractmethod + def restore_map(self, + fine_tune_checkpoint_type='detection', + load_all_detection_checkpoint_vars=False): + """Returns a map of variables to load from a foreign checkpoint. + + Returns a map of variable names to load from a checkpoint to variables in + the model graph. This enables the model to initialize based on weights from + another task. For example, the feature extractor variables from a + classification model can be used to bootstrap training of an object + detector. When loading from an object detection model, the checkpoint model + should have the same parameters as this detection model with exception of + the num_classes parameter. + + Args: + fine_tune_checkpoint_type: whether to restore from a full detection + checkpoint (with compatible variable names) or to restore from a + classification checkpoint for initialization prior to training. + Valid values: `detection`, `classification`. Default 'detection'. + load_all_detection_checkpoint_vars: whether to load all variables (when + `fine_tune_checkpoint_type` is `detection`). If False, only variables + within the feature extractor scope are included. Default False. + + Returns: + A dict mapping variable names (to load from a checkpoint) to variables in + the model graph. + """ + pass + + @abc.abstractmethod + def restore_from_objects(self, fine_tune_checkpoint_type='detection'): + """Returns a map of variables to load from a foreign checkpoint. + + Returns a dictionary of Tensorflow 2 Trackable objects (e.g. tf.Module + or Checkpoint). This enables the model to initialize based on weights from + another task. For example, the feature extractor variables from a + classification model can be used to bootstrap training of an object + detector. When loading from an object detection model, the checkpoint model + should have the same parameters as this detection model with exception of + the num_classes parameter. + + Note that this function is intended to be used to restore Keras-based + models when running Tensorflow 2, whereas restore_map (above) is intended + to be used to restore Slim-based models when running Tensorflow 1.x. + + TODO(jonathanhuang,rathodv): Check tf_version and raise unimplemented + error for both restore_map and restore_from_objects depending on version. + + Args: + fine_tune_checkpoint_type: whether to restore from a full detection + checkpoint (with compatible variable names) or to restore from a + classification checkpoint for initialization prior to training. + Valid values: `detection`, `classification`. Default 'detection'. + + Returns: + A dict mapping keys to Trackable objects (tf.Module or Checkpoint). + """ + pass + + @abc.abstractmethod + def updates(self): + """Returns a list of update operators for this model. + + Returns a list of update operators for this model that must be executed at + each training step. The estimator's train op needs to have a control + dependency on these updates. + + Returns: + A list of update operators. + """ + pass + + def call(self, images): + """Returns detections from a batch of images. + + This method calls the preprocess, predict and postprocess function + sequentially and returns the output. + + Args: + images: a [batch_size, height, width, channels] float tensor. + + Returns: + detetcions: The dict of tensors returned by the postprocess function. + """ + + preprocessed_images, shapes = self.preprocess(images) + prediction_dict = self.predict(preprocessed_images, shapes) + return self.postprocess(prediction_dict, shapes) diff --git a/workspace/virtuallab/object_detection/core/model.pyc b/workspace/virtuallab/object_detection/core/model.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92bbcc60692550278f49643b35dae7db66c0c8ba GIT binary patch literal 23175 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 iou_threshold), iou_sum_new + ] + + +def _cross_suppression(boxes, box_slice, iou_threshold, inner_idx): + """Bounding-boxes cross-suppression loop body. + + Args: + boxes: A float Tensor of shape [1, anchors, 4], representing boxes. + box_slice: A float Tensor of shape [1, _NMS_TILE_SIZE, 4], the box tile + returned from last iteration + iou_threshold: A scalar, representing IOU threshold. + inner_idx: A scalar, representing inner index. + + Returns: + boxes: A float Tensor of shape [1, anchors, 4], representing boxes. + ret_slice: A float Tensor of shape [1, _NMS_TILE_SIZE, 4], the box tile + after suppression + iou_threshold: A scalar, representing IOU threshold. + inner_idx: A scalar, inner index incremented. + """ + new_slice = tf.slice(boxes, [0, inner_idx * _NMS_TILE_SIZE, 0], + [1, _NMS_TILE_SIZE, 4]) + iou = batch_iou(new_slice, box_slice) + ret_slice = tf.expand_dims( + tf.cast(tf.reduce_all(iou < iou_threshold, [1]), box_slice.dtype), + 2) * box_slice + return boxes, ret_slice, iou_threshold, inner_idx + 1 + + +def _suppression_loop_body(boxes, iou_threshold, output_size, idx): + """Process boxes in the range [idx*_NMS_TILE_SIZE, (idx+1)*_NMS_TILE_SIZE). + + Args: + boxes: a tensor with a shape of [1, anchors, 4]. + iou_threshold: a float representing the threshold for deciding whether boxes + overlap too much with respect to IOU. + output_size: an int32 tensor of size [1]. Representing the number of + selected boxes. + idx: an integer scalar representing induction variable. + + Returns: + boxes: updated boxes. + iou_threshold: pass down iou_threshold to the next iteration. + output_size: the updated output_size. + idx: the updated induction variable. + """ + num_tiles = tf.shape(boxes)[1] // _NMS_TILE_SIZE + + # Iterates over tiles that can possibly suppress the current tile. + box_slice = tf.slice(boxes, [0, idx * _NMS_TILE_SIZE, 0], + [1, _NMS_TILE_SIZE, 4]) + _, box_slice, _, _ = tf.while_loop( + lambda _boxes, _box_slice, _threshold, inner_idx: inner_idx < idx, + _cross_suppression, [boxes, box_slice, iou_threshold, + tf.constant(0)]) + + # Iterates over the current tile to compute self-suppression. + iou = batch_iou(box_slice, box_slice) + mask = tf.expand_dims( + tf.reshape(tf.range(_NMS_TILE_SIZE), [1, -1]) > tf.reshape( + tf.range(_NMS_TILE_SIZE), [-1, 1]), 0) + iou *= tf.cast(tf.logical_and(mask, iou >= iou_threshold), iou.dtype) + suppressed_iou, _, _, _ = tf.while_loop( + lambda _iou, _threshold, loop_condition, _iou_sum: loop_condition, + _self_suppression, + [iou, iou_threshold, + tf.constant(True), + tf.reduce_sum(iou, [1, 2])]) + suppressed_box = tf.reduce_sum(suppressed_iou, 1) > 0 + box_slice *= tf.expand_dims(1.0 - tf.cast(suppressed_box, box_slice.dtype), 2) + + # Uses box_slice to update the input boxes. + mask = tf.reshape( + tf.cast(tf.equal(tf.range(num_tiles), idx), boxes.dtype), [1, -1, 1, 1]) + boxes = tf.tile(tf.expand_dims(box_slice, [1]), + [1, num_tiles, 1, 1]) * mask + tf.reshape( + boxes, [1, num_tiles, _NMS_TILE_SIZE, 4]) * (1 - mask) + boxes = tf.reshape(boxes, [1, -1, 4]) + + # Updates output_size. + output_size += tf.reduce_sum( + tf.cast(tf.reduce_any(box_slice > 0, [2]), tf.int32), [1]) + return boxes, iou_threshold, output_size, idx + 1 + + +def partitioned_non_max_suppression_padded(boxes, + scores, + max_output_size, + iou_threshold=0.5, + score_threshold=float('-inf')): + """A tiled version of [`tf.image.non_max_suppression_padded`](https://www.tensorflow.org/api_docs/python/tf/image/non_max_suppression_padded). + + The overall design of the algorithm is to handle boxes tile-by-tile: + + boxes = boxes.pad_to_multiple_of(tile_size) + num_tiles = len(boxes) // tile_size + output_boxes = [] + for i in range(num_tiles): + box_tile = boxes[i*tile_size : (i+1)*tile_size] + for j in range(i - 1): + suppressing_tile = boxes[j*tile_size : (j+1)*tile_size] + iou = batch_iou(box_tile, suppressing_tile) + # if the box is suppressed in iou, clear it to a dot + box_tile *= _update_boxes(iou) + # Iteratively handle the diagonal tile. + iou = _box_overlap(box_tile, box_tile) + iou_changed = True + while iou_changed: + # boxes that are not suppressed by anything else + suppressing_boxes = _get_suppressing_boxes(iou) + # boxes that are suppressed by suppressing_boxes + suppressed_boxes = _get_suppressed_boxes(iou, suppressing_boxes) + # clear iou to 0 for boxes that are suppressed, as they cannot be used + # to suppress other boxes any more + new_iou = _clear_iou(iou, suppressed_boxes) + iou_changed = (new_iou != iou) + iou = new_iou + # remaining boxes that can still suppress others, are selected boxes. + output_boxes.append(_get_suppressing_boxes(iou)) + if len(output_boxes) >= max_output_size: + break + + Args: + boxes: A 2-D float `Tensor` of shape `[num_boxes, 4]`. + scores: A 1-D float `Tensor` of shape `[num_boxes]` representing a single + score corresponding to each box (each row of boxes). + max_output_size: a scalar integer `Tensor` representing the maximum number + of boxes to be selected by non max suppression. + iou_threshold: a float representing the threshold for deciding whether boxes + overlap too much with respect to IOU. + score_threshold: A float representing the threshold for deciding when to + remove boxes based on score. + + Returns: + selected_indices: a tensor of shape [anchors]. + num_valid_boxes: a scalar int tensor. + nms_proposals: a tensor with a shape of [anchors, 4]. It has + same dtype as input boxes. + nms_scores: a tensor with a shape of [anchors]. It has same + dtype as input scores. + argsort_ids: a tensor of shape [anchors], mapping from input order of boxes + to output order of boxes. + """ + num_boxes = tf.shape(boxes)[0] + pad = tf.cast( + tf.ceil(tf.cast(num_boxes, tf.float32) / _NMS_TILE_SIZE), + tf.int32) * _NMS_TILE_SIZE - num_boxes + + scores, argsort_ids = tf.nn.top_k(scores, k=num_boxes, sorted=True) + boxes = tf.gather(boxes, argsort_ids) + num_boxes = tf.shape(boxes)[0] + num_boxes += pad + boxes = tf.pad( + tf.cast(boxes, tf.float32), [[0, pad], [0, 0]], constant_values=-1) + scores = tf.pad(tf.cast(scores, tf.float32), [[0, pad]]) + + # mask boxes to -1 by score threshold + scores_mask = tf.expand_dims( + tf.cast(scores > score_threshold, boxes.dtype), axis=1) + boxes = ((boxes + 1.) * scores_mask) - 1. + + boxes = tf.expand_dims(boxes, axis=0) + scores = tf.expand_dims(scores, axis=0) + + def _loop_cond(unused_boxes, unused_threshold, output_size, idx): + return tf.logical_and( + tf.reduce_min(output_size) < max_output_size, + idx < num_boxes // _NMS_TILE_SIZE) + + selected_boxes, _, output_size, _ = tf.while_loop( + _loop_cond, _suppression_loop_body, + [boxes, iou_threshold, + tf.zeros([1], tf.int32), + tf.constant(0)]) + idx = num_boxes - tf.cast( + tf.nn.top_k( + tf.cast(tf.reduce_any(selected_boxes > 0, [2]), tf.int32) * + tf.expand_dims(tf.range(num_boxes, 0, -1), 0), max_output_size)[0], + tf.int32) + idx = tf.minimum(idx, num_boxes - 1 - pad) + idx = tf.reshape(idx + tf.reshape(tf.range(1) * num_boxes, [-1, 1]), [-1]) + num_valid_boxes = tf.reduce_sum(output_size) + return (idx, num_valid_boxes, tf.reshape(boxes, [-1, 4]), + tf.reshape(scores, [-1]), argsort_ids) + + +def _validate_boxes_scores_iou_thresh(boxes, scores, iou_thresh, + change_coordinate_frame, clip_window): + """Validates boxes, scores and iou_thresh. + + This function validates the boxes, scores, iou_thresh + and if change_coordinate_frame is True, clip_window must be specified. + + Args: + boxes: A [k, q, 4] float32 tensor containing k detections. `q` can be either + number of classes or 1 depending on whether a separate box is predicted + per class. + scores: A [k, num_classes] float32 tensor containing the scores for each of + the k detections. The scores have to be non-negative when + pad_to_max_output_size is True. + iou_thresh: scalar threshold for IOU (new boxes that have high IOU overlap + with previously selected boxes are removed). + change_coordinate_frame: Whether to normalize coordinates after clipping + relative to clip_window (this can only be set to True if a clip_window is + provided) + clip_window: A float32 tensor of the form [y_min, x_min, y_max, x_max] + representing the window to clip and normalize boxes to before performing + non-max suppression. + + Raises: + ValueError: if iou_thresh is not in [0, 1] or if input boxlist does not + have a valid scores field. + """ + if not 0 <= iou_thresh <= 1.0: + raise ValueError('iou_thresh must be between 0 and 1') + if scores.shape.ndims != 2: + raise ValueError('scores field must be of rank 2') + if shape_utils.get_dim_as_int(scores.shape[1]) is None: + raise ValueError('scores must have statically defined second ' 'dimension') + if boxes.shape.ndims != 3: + raise ValueError('boxes must be of rank 3.') + if not (shape_utils.get_dim_as_int( + boxes.shape[1]) == shape_utils.get_dim_as_int(scores.shape[1]) or + shape_utils.get_dim_as_int(boxes.shape[1]) == 1): + raise ValueError('second dimension of boxes must be either 1 or equal ' + 'to the second dimension of scores') + if shape_utils.get_dim_as_int(boxes.shape[2]) != 4: + raise ValueError('last dimension of boxes must be of size 4.') + if change_coordinate_frame and clip_window is None: + raise ValueError('if change_coordinate_frame is True, then a clip_window' + 'must be specified.') + + +def _clip_window_prune_boxes(sorted_boxes, clip_window, pad_to_max_output_size, + change_coordinate_frame): + """Prune boxes with zero area. + + Args: + sorted_boxes: A BoxList containing k detections. + clip_window: A float32 tensor of the form [y_min, x_min, y_max, x_max] + representing the window to clip and normalize boxes to before performing + non-max suppression. + pad_to_max_output_size: flag indicating whether to pad to max output size or + not. + change_coordinate_frame: Whether to normalize coordinates after clipping + relative to clip_window (this can only be set to True if a clip_window is + provided). + + Returns: + sorted_boxes: A BoxList containing k detections after pruning. + num_valid_nms_boxes_cumulative: Number of valid NMS boxes + """ + sorted_boxes = box_list_ops.clip_to_window( + sorted_boxes, + clip_window, + filter_nonoverlapping=not pad_to_max_output_size) + # Set the scores of boxes with zero area to -1 to keep the default + # behaviour of pruning out zero area boxes. + sorted_boxes_size = tf.shape(sorted_boxes.get())[0] + non_zero_box_area = tf.cast(box_list_ops.area(sorted_boxes), tf.bool) + sorted_boxes_scores = tf.where( + non_zero_box_area, sorted_boxes.get_field(fields.BoxListFields.scores), + -1 * tf.ones(sorted_boxes_size)) + sorted_boxes.add_field(fields.BoxListFields.scores, sorted_boxes_scores) + num_valid_nms_boxes_cumulative = tf.reduce_sum( + tf.cast(tf.greater_equal(sorted_boxes_scores, 0), tf.int32)) + sorted_boxes = box_list_ops.sort_by_field(sorted_boxes, + fields.BoxListFields.scores) + if change_coordinate_frame: + sorted_boxes = box_list_ops.change_coordinate_frame(sorted_boxes, + clip_window) + return sorted_boxes, num_valid_nms_boxes_cumulative + + +class NullContextmanager(object): + + def __enter__(self): + pass + + def __exit__(self, type_arg, value_arg, traceback_arg): + return False + + +def multiclass_non_max_suppression(boxes, + scores, + score_thresh, + iou_thresh, + max_size_per_class, + max_total_size=0, + clip_window=None, + change_coordinate_frame=False, + masks=None, + boundaries=None, + pad_to_max_output_size=False, + use_partitioned_nms=False, + additional_fields=None, + soft_nms_sigma=0.0, + use_hard_nms=False, + use_cpu_nms=False, + scope=None): + """Multi-class version of non maximum suppression. + + This op greedily selects a subset of detection bounding boxes, pruning + away boxes that have high IOU (intersection over union) overlap (> thresh) + with already selected boxes. It operates independently for each class for + which scores are provided (via the scores field of the input box_list), + pruning boxes with score less than a provided threshold prior to + applying NMS. + + Please note that this operation is performed on *all* classes, therefore any + background classes should be removed prior to calling this function. + + Selected boxes are guaranteed to be sorted in decreasing order by score (but + the sort is not guaranteed to be stable). + + Args: + boxes: A [k, q, 4] float32 tensor containing k detections. `q` can be either + number of classes or 1 depending on whether a separate box is predicted + per class. + scores: A [k, num_classes] float32 tensor containing the scores for each of + the k detections. The scores have to be non-negative when + pad_to_max_output_size is True. + score_thresh: scalar threshold for score (low scoring boxes are removed). + iou_thresh: scalar threshold for IOU (new boxes that have high IOU overlap + with previously selected boxes are removed). + max_size_per_class: maximum number of retained boxes per class. + max_total_size: maximum number of boxes retained over all classes. By + default returns all boxes retained after capping boxes per class. + clip_window: A float32 tensor of the form [y_min, x_min, y_max, x_max] + representing the window to clip and normalize boxes to before performing + non-max suppression. + change_coordinate_frame: Whether to normalize coordinates after clipping + relative to clip_window (this can only be set to True if a clip_window + is provided) + masks: (optional) a [k, q, mask_height, mask_width] float32 tensor + containing box masks. `q` can be either number of classes or 1 depending + on whether a separate mask is predicted per class. + boundaries: (optional) a [k, q, boundary_height, boundary_width] float32 + tensor containing box boundaries. `q` can be either number of classes or 1 + depending on whether a separate boundary is predicted per class. + pad_to_max_output_size: If true, the output nmsed boxes are padded to be of + length `max_size_per_class`. Defaults to false. + use_partitioned_nms: If true, use partitioned version of + non_max_suppression. + additional_fields: (optional) If not None, a dictionary that maps keys to + tensors whose first dimensions are all of size `k`. After non-maximum + suppression, all tensors corresponding to the selected boxes will be + added to resulting BoxList. + soft_nms_sigma: A scalar float representing the Soft NMS sigma parameter; + See Bodla et al, https://arxiv.org/abs/1704.04503). When + `soft_nms_sigma=0.0` (which is default), we fall back to standard (hard) + NMS. Soft NMS is currently only supported when pad_to_max_output_size is + False. + use_hard_nms: Enforce the usage of hard NMS. + use_cpu_nms: Enforce NMS to run on CPU. + scope: name scope. + + Returns: + A tuple of sorted_boxes and num_valid_nms_boxes. The sorted_boxes is a + BoxList holds M boxes with a rank-1 scores field representing + corresponding scores for each box with scores sorted in decreasing order + and a rank-1 classes field representing a class label for each box. The + num_valid_nms_boxes is a 0-D integer tensor representing the number of + valid elements in `BoxList`, with the valid elements appearing first. + + Raises: + ValueError: if iou_thresh is not in [0, 1] or if input boxlist does not have + a valid scores field. + ValueError: if Soft NMS (tf.image.non_max_suppression_with_scores) is not + supported in the current TF version and `soft_nms_sigma` is nonzero. + """ + _validate_boxes_scores_iou_thresh(boxes, scores, iou_thresh, + change_coordinate_frame, clip_window) + if pad_to_max_output_size and soft_nms_sigma != 0.0: + raise ValueError('Soft NMS (soft_nms_sigma != 0.0) is currently not ' + 'supported when pad_to_max_output_size is True.') + + with tf.name_scope(scope, 'MultiClassNonMaxSuppression'), tf.device( + 'cpu:0') if use_cpu_nms else NullContextmanager(): + num_scores = tf.shape(scores)[0] + num_classes = shape_utils.get_dim_as_int(scores.get_shape()[1]) + + selected_boxes_list = [] + num_valid_nms_boxes_cumulative = tf.constant(0) + per_class_boxes_list = tf.unstack(boxes, axis=1) + if masks is not None: + per_class_masks_list = tf.unstack(masks, axis=1) + if boundaries is not None: + per_class_boundaries_list = tf.unstack(boundaries, axis=1) + boxes_ids = (range(num_classes) if len(per_class_boxes_list) > 1 + else [0] * num_classes) + for class_idx, boxes_idx in zip(range(num_classes), boxes_ids): + per_class_boxes = per_class_boxes_list[boxes_idx] + boxlist_and_class_scores = box_list.BoxList(per_class_boxes) + class_scores = tf.reshape( + tf.slice(scores, [0, class_idx], tf.stack([num_scores, 1])), [-1]) + + boxlist_and_class_scores.add_field(fields.BoxListFields.scores, + class_scores) + if masks is not None: + per_class_masks = per_class_masks_list[boxes_idx] + boxlist_and_class_scores.add_field(fields.BoxListFields.masks, + per_class_masks) + if boundaries is not None: + per_class_boundaries = per_class_boundaries_list[boxes_idx] + boxlist_and_class_scores.add_field(fields.BoxListFields.boundaries, + per_class_boundaries) + if additional_fields is not None: + for key, tensor in additional_fields.items(): + boxlist_and_class_scores.add_field(key, tensor) + + nms_result = None + selected_scores = None + if pad_to_max_output_size: + max_selection_size = max_size_per_class + if use_partitioned_nms: + (selected_indices, num_valid_nms_boxes, + boxlist_and_class_scores.data['boxes'], + boxlist_and_class_scores.data['scores'], + _) = partitioned_non_max_suppression_padded( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh) + else: + selected_indices, num_valid_nms_boxes = ( + tf.image.non_max_suppression_padded( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field( + fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh, + pad_to_max_output_size=True)) + nms_result = box_list_ops.gather(boxlist_and_class_scores, + selected_indices) + selected_scores = nms_result.get_field(fields.BoxListFields.scores) + else: + max_selection_size = tf.minimum(max_size_per_class, + boxlist_and_class_scores.num_boxes()) + if (hasattr(tf.image, 'non_max_suppression_with_scores') and + tf.compat.forward_compatible(2019, 6, 6) and not use_hard_nms): + (selected_indices, selected_scores + ) = tf.image.non_max_suppression_with_scores( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh, + soft_nms_sigma=soft_nms_sigma) + num_valid_nms_boxes = tf.shape(selected_indices)[0] + selected_indices = tf.concat( + [selected_indices, + tf.zeros(max_selection_size-num_valid_nms_boxes, tf.int32)], 0) + selected_scores = tf.concat( + [selected_scores, + tf.zeros(max_selection_size-num_valid_nms_boxes, + tf.float32)], -1) + nms_result = box_list_ops.gather(boxlist_and_class_scores, + selected_indices) + else: + if soft_nms_sigma != 0: + raise ValueError('Soft NMS not supported in current TF version!') + selected_indices = tf.image.non_max_suppression( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh) + num_valid_nms_boxes = tf.shape(selected_indices)[0] + selected_indices = tf.concat( + [selected_indices, + tf.zeros(max_selection_size-num_valid_nms_boxes, tf.int32)], 0) + nms_result = box_list_ops.gather(boxlist_and_class_scores, + selected_indices) + selected_scores = nms_result.get_field(fields.BoxListFields.scores) + # Make the scores -1 for invalid boxes. + valid_nms_boxes_indices = tf.less( + tf.range(max_selection_size), num_valid_nms_boxes) + + nms_result.add_field( + fields.BoxListFields.scores, + tf.where(valid_nms_boxes_indices, + selected_scores, -1*tf.ones(max_selection_size))) + num_valid_nms_boxes_cumulative += num_valid_nms_boxes + + nms_result.add_field( + fields.BoxListFields.classes, (tf.zeros_like( + nms_result.get_field(fields.BoxListFields.scores)) + class_idx)) + selected_boxes_list.append(nms_result) + selected_boxes = box_list_ops.concatenate(selected_boxes_list) + sorted_boxes = box_list_ops.sort_by_field(selected_boxes, + fields.BoxListFields.scores) + if clip_window is not None: + # When pad_to_max_output_size is False, it prunes the boxes with zero + # area. + sorted_boxes, num_valid_nms_boxes_cumulative = _clip_window_prune_boxes( + sorted_boxes, clip_window, pad_to_max_output_size, + change_coordinate_frame) + + if max_total_size: + max_total_size = tf.minimum(max_total_size, sorted_boxes.num_boxes()) + sorted_boxes = box_list_ops.gather(sorted_boxes, tf.range(max_total_size)) + num_valid_nms_boxes_cumulative = tf.where( + max_total_size > num_valid_nms_boxes_cumulative, + num_valid_nms_boxes_cumulative, max_total_size) + # Select only the valid boxes if pad_to_max_output_size is False. + if not pad_to_max_output_size: + sorted_boxes = box_list_ops.gather( + sorted_boxes, tf.range(num_valid_nms_boxes_cumulative)) + + return sorted_boxes, num_valid_nms_boxes_cumulative + + +def class_agnostic_non_max_suppression(boxes, + scores, + score_thresh, + iou_thresh, + max_classes_per_detection=1, + max_total_size=0, + clip_window=None, + change_coordinate_frame=False, + masks=None, + boundaries=None, + pad_to_max_output_size=False, + use_partitioned_nms=False, + additional_fields=None, + soft_nms_sigma=0.0, + scope=None): + """Class-agnostic version of non maximum suppression. + + This op greedily selects a subset of detection bounding boxes, pruning + away boxes that have high IOU (intersection over union) overlap (> thresh) + with already selected boxes. It operates on all the boxes using + max scores across all classes for which scores are provided (via the scores + field of the input box_list), pruning boxes with score less than a provided + threshold prior to applying NMS. + + Please note that this operation is performed in a class-agnostic way, + therefore any background classes should be removed prior to calling this + function. + + Selected boxes are guaranteed to be sorted in decreasing order by score (but + the sort is not guaranteed to be stable). + + Args: + boxes: A [k, q, 4] float32 tensor containing k detections. `q` can be either + number of classes or 1 depending on whether a separate box is predicted + per class. + scores: A [k, num_classes] float32 tensor containing the scores for each of + the k detections. The scores have to be non-negative when + pad_to_max_output_size is True. + score_thresh: scalar threshold for score (low scoring boxes are removed). + iou_thresh: scalar threshold for IOU (new boxes that have high IOU overlap + with previously selected boxes are removed). + max_classes_per_detection: maximum number of retained classes per detection + box in class-agnostic NMS. + max_total_size: maximum number of boxes retained over all classes. By + default returns all boxes retained after capping boxes per class. + clip_window: A float32 tensor of the form [y_min, x_min, y_max, x_max] + representing the window to clip and normalize boxes to before performing + non-max suppression. + change_coordinate_frame: Whether to normalize coordinates after clipping + relative to clip_window (this can only be set to True if a clip_window is + provided) + masks: (optional) a [k, q, mask_height, mask_width] float32 tensor + containing box masks. `q` can be either number of classes or 1 depending + on whether a separate mask is predicted per class. + boundaries: (optional) a [k, q, boundary_height, boundary_width] float32 + tensor containing box boundaries. `q` can be either number of classes or 1 + depending on whether a separate boundary is predicted per class. + pad_to_max_output_size: If true, the output nmsed boxes are padded to be of + length `max_size_per_class`. Defaults to false. + use_partitioned_nms: If true, use partitioned version of + non_max_suppression. + additional_fields: (optional) If not None, a dictionary that maps keys to + tensors whose first dimensions are all of size `k`. After non-maximum + suppression, all tensors corresponding to the selected boxes will be added + to resulting BoxList. + soft_nms_sigma: A scalar float representing the Soft NMS sigma parameter; + See Bodla et al, https://arxiv.org/abs/1704.04503). When + `soft_nms_sigma=0.0` (which is default), we fall back to standard (hard) + NMS. Soft NMS is currently only supported when pad_to_max_output_size is + False. + scope: name scope. + + Returns: + A tuple of sorted_boxes and num_valid_nms_boxes. The sorted_boxes is a + BoxList holds M boxes with a rank-1 scores field representing + corresponding scores for each box with scores sorted in decreasing order + and a rank-1 classes field representing a class label for each box. The + num_valid_nms_boxes is a 0-D integer tensor representing the number of + valid elements in `BoxList`, with the valid elements appearing first. + + Raises: + ValueError: if iou_thresh is not in [0, 1] or if input boxlist does not have + a valid scores field or if non-zero soft_nms_sigma is provided when + pad_to_max_output_size is True. + """ + _validate_boxes_scores_iou_thresh(boxes, scores, iou_thresh, + change_coordinate_frame, clip_window) + if pad_to_max_output_size and soft_nms_sigma != 0.0: + raise ValueError('Soft NMS (soft_nms_sigma != 0.0) is currently not ' + 'supported when pad_to_max_output_size is True.') + + if max_classes_per_detection > 1: + raise ValueError('Max classes per detection box >1 not supported.') + q = shape_utils.get_dim_as_int(boxes.shape[1]) + if q > 1: + class_ids = tf.expand_dims( + tf.argmax(scores, axis=1, output_type=tf.int32), axis=1) + boxes = tf.batch_gather(boxes, class_ids) + if masks is not None: + masks = tf.batch_gather(masks, class_ids) + if boundaries is not None: + boundaries = tf.batch_gather(boundaries, class_ids) + boxes = tf.squeeze(boxes, axis=[1]) + if masks is not None: + masks = tf.squeeze(masks, axis=[1]) + if boundaries is not None: + boundaries = tf.squeeze(boundaries, axis=[1]) + + with tf.name_scope(scope, 'ClassAgnosticNonMaxSuppression'): + boxlist_and_class_scores = box_list.BoxList(boxes) + max_scores = tf.reduce_max(scores, axis=-1) + classes_with_max_scores = tf.argmax(scores, axis=-1) + boxlist_and_class_scores.add_field(fields.BoxListFields.scores, max_scores) + if masks is not None: + boxlist_and_class_scores.add_field(fields.BoxListFields.masks, masks) + if boundaries is not None: + boxlist_and_class_scores.add_field(fields.BoxListFields.boundaries, + boundaries) + + if additional_fields is not None: + for key, tensor in additional_fields.items(): + boxlist_and_class_scores.add_field(key, tensor) + + nms_result = None + selected_scores = None + if pad_to_max_output_size: + max_selection_size = max_total_size + if use_partitioned_nms: + (selected_indices, num_valid_nms_boxes, + boxlist_and_class_scores.data['boxes'], + boxlist_and_class_scores.data['scores'], + argsort_ids) = partitioned_non_max_suppression_padded( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh) + classes_with_max_scores = tf.gather(classes_with_max_scores, + argsort_ids) + else: + selected_indices, num_valid_nms_boxes = ( + tf.image.non_max_suppression_padded( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh, + pad_to_max_output_size=True)) + nms_result = box_list_ops.gather(boxlist_and_class_scores, + selected_indices) + selected_scores = nms_result.get_field(fields.BoxListFields.scores) + else: + max_selection_size = tf.minimum(max_total_size, + boxlist_and_class_scores.num_boxes()) + if (hasattr(tf.image, 'non_max_suppression_with_scores') and + tf.compat.forward_compatible(2019, 6, 6)): + (selected_indices, selected_scores + ) = tf.image.non_max_suppression_with_scores( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh, + soft_nms_sigma=soft_nms_sigma) + num_valid_nms_boxes = tf.shape(selected_indices)[0] + selected_indices = tf.concat([ + selected_indices, + tf.zeros(max_selection_size - num_valid_nms_boxes, tf.int32) + ], 0) + selected_scores = tf.concat( + [selected_scores, + tf.zeros(max_selection_size-num_valid_nms_boxes, tf.float32)], -1) + nms_result = box_list_ops.gather(boxlist_and_class_scores, + selected_indices) + else: + if soft_nms_sigma != 0: + raise ValueError('Soft NMS not supported in current TF version!') + selected_indices = tf.image.non_max_suppression( + boxlist_and_class_scores.get(), + boxlist_and_class_scores.get_field(fields.BoxListFields.scores), + max_selection_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh) + num_valid_nms_boxes = tf.shape(selected_indices)[0] + selected_indices = tf.concat( + [selected_indices, + tf.zeros(max_selection_size-num_valid_nms_boxes, tf.int32)], 0) + nms_result = box_list_ops.gather(boxlist_and_class_scores, + selected_indices) + selected_scores = nms_result.get_field(fields.BoxListFields.scores) + valid_nms_boxes_indices = tf.less( + tf.range(max_selection_size), num_valid_nms_boxes) + nms_result.add_field( + fields.BoxListFields.scores, + tf.where(valid_nms_boxes_indices, + selected_scores, -1*tf.ones(max_selection_size))) + + selected_classes = tf.gather(classes_with_max_scores, selected_indices) + selected_classes = tf.cast(selected_classes, tf.float32) + nms_result.add_field(fields.BoxListFields.classes, selected_classes) + selected_boxes = nms_result + sorted_boxes = box_list_ops.sort_by_field(selected_boxes, + fields.BoxListFields.scores) + + if clip_window is not None: + # When pad_to_max_output_size is False, it prunes the boxes with zero + # area. + sorted_boxes, num_valid_nms_boxes = _clip_window_prune_boxes( + sorted_boxes, clip_window, pad_to_max_output_size, + change_coordinate_frame) + + if max_total_size: + max_total_size = tf.minimum(max_total_size, sorted_boxes.num_boxes()) + sorted_boxes = box_list_ops.gather(sorted_boxes, tf.range(max_total_size)) + num_valid_nms_boxes = tf.where(max_total_size > num_valid_nms_boxes, + num_valid_nms_boxes, max_total_size) + # Select only the valid boxes if pad_to_max_output_size is False. + if not pad_to_max_output_size: + sorted_boxes = box_list_ops.gather(sorted_boxes, + tf.range(num_valid_nms_boxes)) + + return sorted_boxes, num_valid_nms_boxes + + +def batch_multiclass_non_max_suppression(boxes, + scores, + score_thresh, + iou_thresh, + max_size_per_class, + max_total_size=0, + clip_window=None, + change_coordinate_frame=False, + num_valid_boxes=None, + masks=None, + additional_fields=None, + soft_nms_sigma=0.0, + scope=None, + use_static_shapes=False, + use_partitioned_nms=False, + parallel_iterations=32, + use_class_agnostic_nms=False, + max_classes_per_detection=1, + use_dynamic_map_fn=False, + use_combined_nms=False, + use_hard_nms=False, + use_cpu_nms=False): + """Multi-class version of non maximum suppression that operates on a batch. + + This op is similar to `multiclass_non_max_suppression` but operates on a batch + of boxes and scores. See documentation for `multiclass_non_max_suppression` + for details. + + Args: + boxes: A [batch_size, num_anchors, q, 4] float32 tensor containing + detections. If `q` is 1 then same boxes are used for all classes + otherwise, if `q` is equal to number of classes, class-specific boxes are + used. + scores: A [batch_size, num_anchors, num_classes] float32 tensor containing + the scores for each of the `num_anchors` detections. The scores have to be + non-negative when use_static_shapes is set True. + score_thresh: scalar threshold for score (low scoring boxes are removed). + iou_thresh: scalar threshold for IOU (new boxes that have high IOU overlap + with previously selected boxes are removed). + max_size_per_class: maximum number of retained boxes per class. + max_total_size: maximum number of boxes retained over all classes. By + default returns all boxes retained after capping boxes per class. + clip_window: A float32 tensor of shape [batch_size, 4] where each entry is + of the form [y_min, x_min, y_max, x_max] representing the window to clip + boxes to before performing non-max suppression. This argument can also be + a tensor of shape [4] in which case, the same clip window is applied to + all images in the batch. If clip_widow is None, all boxes are used to + perform non-max suppression. + change_coordinate_frame: Whether to normalize coordinates after clipping + relative to clip_window (this can only be set to True if a clip_window is + provided) + num_valid_boxes: (optional) a Tensor of type `int32`. A 1-D tensor of shape + [batch_size] representing the number of valid boxes to be considered for + each image in the batch. This parameter allows for ignoring zero + paddings. + masks: (optional) a [batch_size, num_anchors, q, mask_height, mask_width] + float32 tensor containing box masks. `q` can be either number of classes + or 1 depending on whether a separate mask is predicted per class. + additional_fields: (optional) If not None, a dictionary that maps keys to + tensors whose dimensions are [batch_size, num_anchors, ...]. + soft_nms_sigma: A scalar float representing the Soft NMS sigma parameter; + See Bodla et al, https://arxiv.org/abs/1704.04503). When + `soft_nms_sigma=0.0` (which is default), we fall back to standard (hard) + NMS. Soft NMS is currently only supported when pad_to_max_output_size is + False. + scope: tf scope name. + use_static_shapes: If true, the output nmsed boxes are padded to be of + length `max_size_per_class` and it doesn't clip boxes to max_total_size. + Defaults to false. + use_partitioned_nms: If true, use partitioned version of + non_max_suppression. + parallel_iterations: (optional) number of batch items to process in + parallel. + use_class_agnostic_nms: If true, this uses class-agnostic non max + suppression + max_classes_per_detection: Maximum number of retained classes per detection + box in class-agnostic NMS. + use_dynamic_map_fn: If true, images in the batch will be processed within a + dynamic loop. Otherwise, a static loop will be used if possible. + use_combined_nms: If true, it uses tf.image.combined_non_max_suppression ( + multi-class version of NMS that operates on a batch). + It greedily selects a subset of detection bounding boxes, pruning away + boxes that have high IOU (intersection over union) overlap (> thresh) with + already selected boxes. It operates independently for each batch. + Within each batch, it operates independently for each class for which + scores are provided (via the scores field of the input box_list), + pruning boxes with score less than a provided threshold prior to applying + NMS. This operation is performed on *all* batches and *all* classes + in the batch, therefore any background classes should be removed prior to + calling this function. + Masks and additional fields are not supported. + See argument checks in the code below for unsupported arguments. + use_hard_nms: Enforce the usage of hard NMS. + use_cpu_nms: Enforce NMS to run on CPU. + + Returns: + 'nmsed_boxes': A [batch_size, max_detections, 4] float32 tensor + containing the non-max suppressed boxes. + 'nmsed_scores': A [batch_size, max_detections] float32 tensor containing + the scores for the boxes. + 'nmsed_classes': A [batch_size, max_detections] float32 tensor + containing the class for boxes. + 'nmsed_masks': (optional) a + [batch_size, max_detections, mask_height, mask_width] float32 tensor + containing masks for each selected box. This is set to None if input + `masks` is None. + 'nmsed_additional_fields': (optional) a dictionary of + [batch_size, max_detections, ...] float32 tensors corresponding to the + tensors specified in the input `additional_fields`. This is not returned + if input `additional_fields` is None. + 'num_detections': A [batch_size] int32 tensor indicating the number of + valid detections per batch item. Only the top num_detections[i] entries in + nms_boxes[i], nms_scores[i] and nms_class[i] are valid. The rest of the + entries are zero paddings. + + Raises: + ValueError: if `q` in boxes.shape is not 1 or not equal to number of + classes as inferred from scores.shape. + """ + if use_combined_nms: + if change_coordinate_frame: + raise ValueError( + 'change_coordinate_frame (normalizing coordinates' + ' relative to clip_window) is not supported by combined_nms.') + if num_valid_boxes is not None: + raise ValueError('num_valid_boxes is not supported by combined_nms.') + if masks is not None: + raise ValueError('masks is not supported by combined_nms.') + if soft_nms_sigma != 0.0: + raise ValueError('Soft NMS is not supported by combined_nms.') + if use_class_agnostic_nms: + raise ValueError('class-agnostic NMS is not supported by combined_nms.') + if clip_window is not None: + tf.logging.warning( + 'clip_window is not supported by combined_nms unless it is' + ' [0. 0. 1. 1.] for each image.') + if additional_fields is not None: + tf.logging.warning('additional_fields is not supported by combined_nms.') + if parallel_iterations != 32: + tf.logging.warning('Number of batch items to be processed in parallel is' + ' not configurable by combined_nms.') + if max_classes_per_detection > 1: + tf.logging.warning( + 'max_classes_per_detection is not configurable by combined_nms.') + + with tf.name_scope(scope, 'CombinedNonMaxSuppression'): + (batch_nmsed_boxes, batch_nmsed_scores, batch_nmsed_classes, + batch_num_detections) = tf.image.combined_non_max_suppression( + boxes=boxes, + scores=scores, + max_output_size_per_class=max_size_per_class, + max_total_size=max_total_size, + iou_threshold=iou_thresh, + score_threshold=score_thresh, + pad_per_class=use_static_shapes) + # Not supported by combined_non_max_suppression. + batch_nmsed_masks = None + # Not supported by combined_non_max_suppression. + batch_nmsed_additional_fields = None + return (batch_nmsed_boxes, batch_nmsed_scores, batch_nmsed_classes, + batch_nmsed_masks, batch_nmsed_additional_fields, + batch_num_detections) + + q = shape_utils.get_dim_as_int(boxes.shape[2]) + num_classes = shape_utils.get_dim_as_int(scores.shape[2]) + if q != 1 and q != num_classes: + raise ValueError('third dimension of boxes must be either 1 or equal ' + 'to the third dimension of scores.') + if change_coordinate_frame and clip_window is None: + raise ValueError('if change_coordinate_frame is True, then a clip_window' + 'must be specified.') + original_masks = masks + + # Create ordered dictionary using the sorted keys from + # additional fields to ensure getting the same key value assignment + # in _single_image_nms_fn(). The dictionary is thus a sorted version of + # additional_fields. + if additional_fields is None: + ordered_additional_fields = collections.OrderedDict() + else: + ordered_additional_fields = collections.OrderedDict( + sorted(additional_fields.items(), key=lambda item: item[0])) + + with tf.name_scope(scope, 'BatchMultiClassNonMaxSuppression'): + boxes_shape = boxes.shape + batch_size = shape_utils.get_dim_as_int(boxes_shape[0]) + num_anchors = shape_utils.get_dim_as_int(boxes_shape[1]) + + if batch_size is None: + batch_size = tf.shape(boxes)[0] + if num_anchors is None: + num_anchors = tf.shape(boxes)[1] + + # If num valid boxes aren't provided, create one and mark all boxes as + # valid. + if num_valid_boxes is None: + num_valid_boxes = tf.ones([batch_size], dtype=tf.int32) * num_anchors + + # If masks aren't provided, create dummy masks so we can only have one copy + # of _single_image_nms_fn and discard the dummy masks after map_fn. + if masks is None: + masks_shape = tf.stack([batch_size, num_anchors, q, 1, 1]) + masks = tf.zeros(masks_shape) + + if clip_window is None: + clip_window = tf.stack([ + tf.reduce_min(boxes[:, :, :, 0]), + tf.reduce_min(boxes[:, :, :, 1]), + tf.reduce_max(boxes[:, :, :, 2]), + tf.reduce_max(boxes[:, :, :, 3]) + ]) + if clip_window.shape.ndims == 1: + clip_window = tf.tile(tf.expand_dims(clip_window, 0), [batch_size, 1]) + + def _single_image_nms_fn(args): + """Runs NMS on a single image and returns padded output. + + Args: + args: A list of tensors consisting of the following: + per_image_boxes - A [num_anchors, q, 4] float32 tensor containing + detections. If `q` is 1 then same boxes are used for all classes + otherwise, if `q` is equal to number of classes, class-specific + boxes are used. + per_image_scores - A [num_anchors, num_classes] float32 tensor + containing the scores for each of the `num_anchors` detections. + per_image_masks - A [num_anchors, q, mask_height, mask_width] float32 + tensor containing box masks. `q` can be either number of classes + or 1 depending on whether a separate mask is predicted per class. + per_image_clip_window - A 1D float32 tensor of the form + [ymin, xmin, ymax, xmax] representing the window to clip the boxes + to. + per_image_additional_fields - (optional) A variable number of float32 + tensors each with size [num_anchors, ...]. + per_image_num_valid_boxes - A tensor of type `int32`. A 1-D tensor of + shape [batch_size] representing the number of valid boxes to be + considered for each image in the batch. This parameter allows for + ignoring zero paddings. + + Returns: + 'nmsed_boxes': A [max_detections, 4] float32 tensor containing the + non-max suppressed boxes. + 'nmsed_scores': A [max_detections] float32 tensor containing the scores + for the boxes. + 'nmsed_classes': A [max_detections] float32 tensor containing the class + for boxes. + 'nmsed_masks': (optional) a [max_detections, mask_height, mask_width] + float32 tensor containing masks for each selected box. This is set to + None if input `masks` is None. + 'nmsed_additional_fields': (optional) A variable number of float32 + tensors each with size [max_detections, ...] corresponding to the + input `per_image_additional_fields`. + 'num_detections': A [batch_size] int32 tensor indicating the number of + valid detections per batch item. Only the top num_detections[i] + entries in nms_boxes[i], nms_scores[i] and nms_class[i] are valid. The + rest of the entries are zero paddings. + """ + per_image_boxes = args[0] + per_image_scores = args[1] + per_image_masks = args[2] + per_image_clip_window = args[3] + # Make sure that the order of elements passed in args is aligned with + # the iteration order of ordered_additional_fields + per_image_additional_fields = { + key: value + for key, value in zip(ordered_additional_fields, args[4:-1]) + } + per_image_num_valid_boxes = args[-1] + if use_static_shapes: + total_proposals = tf.shape(per_image_scores) + per_image_scores = tf.where( + tf.less(tf.range(total_proposals[0]), per_image_num_valid_boxes), + per_image_scores, + tf.fill(total_proposals, np.finfo('float32').min)) + else: + per_image_boxes = tf.reshape( + tf.slice(per_image_boxes, 3 * [0], + tf.stack([per_image_num_valid_boxes, -1, -1])), [-1, q, 4]) + per_image_scores = tf.reshape( + tf.slice(per_image_scores, [0, 0], + tf.stack([per_image_num_valid_boxes, -1])), + [-1, num_classes]) + per_image_masks = tf.reshape( + tf.slice(per_image_masks, 4 * [0], + tf.stack([per_image_num_valid_boxes, -1, -1, -1])), + [-1, q, shape_utils.get_dim_as_int(per_image_masks.shape[2]), + shape_utils.get_dim_as_int(per_image_masks.shape[3])]) + if per_image_additional_fields is not None: + for key, tensor in per_image_additional_fields.items(): + additional_field_shape = tensor.get_shape() + additional_field_dim = len(additional_field_shape) + per_image_additional_fields[key] = tf.reshape( + tf.slice( + per_image_additional_fields[key], + additional_field_dim * [0], + tf.stack([per_image_num_valid_boxes] + + (additional_field_dim - 1) * [-1])), [-1] + [ + shape_utils.get_dim_as_int(dim) + for dim in additional_field_shape[1:] + ]) + if use_class_agnostic_nms: + nmsed_boxlist, num_valid_nms_boxes = class_agnostic_non_max_suppression( + per_image_boxes, + per_image_scores, + score_thresh, + iou_thresh, + max_classes_per_detection, + max_total_size, + clip_window=per_image_clip_window, + change_coordinate_frame=change_coordinate_frame, + masks=per_image_masks, + pad_to_max_output_size=use_static_shapes, + use_partitioned_nms=use_partitioned_nms, + additional_fields=per_image_additional_fields, + soft_nms_sigma=soft_nms_sigma) + else: + nmsed_boxlist, num_valid_nms_boxes = multiclass_non_max_suppression( + per_image_boxes, + per_image_scores, + score_thresh, + iou_thresh, + max_size_per_class, + max_total_size, + clip_window=per_image_clip_window, + change_coordinate_frame=change_coordinate_frame, + masks=per_image_masks, + pad_to_max_output_size=use_static_shapes, + use_partitioned_nms=use_partitioned_nms, + additional_fields=per_image_additional_fields, + soft_nms_sigma=soft_nms_sigma, + use_hard_nms=use_hard_nms, + use_cpu_nms=use_cpu_nms) + + if not use_static_shapes: + nmsed_boxlist = box_list_ops.pad_or_clip_box_list( + nmsed_boxlist, max_total_size) + num_detections = num_valid_nms_boxes + nmsed_boxes = nmsed_boxlist.get() + nmsed_scores = nmsed_boxlist.get_field(fields.BoxListFields.scores) + nmsed_classes = nmsed_boxlist.get_field(fields.BoxListFields.classes) + nmsed_masks = nmsed_boxlist.get_field(fields.BoxListFields.masks) + nmsed_additional_fields = [] + # Sorting is needed here to ensure that the values stored in + # nmsed_additional_fields are always kept in the same order + # across different execution runs. + for key in sorted(per_image_additional_fields.keys()): + nmsed_additional_fields.append(nmsed_boxlist.get_field(key)) + return ([nmsed_boxes, nmsed_scores, nmsed_classes, nmsed_masks] + + nmsed_additional_fields + [num_detections]) + + num_additional_fields = 0 + if ordered_additional_fields: + num_additional_fields = len(ordered_additional_fields) + num_nmsed_outputs = 4 + num_additional_fields + + if use_dynamic_map_fn: + map_fn = tf.map_fn + else: + map_fn = shape_utils.static_or_dynamic_map_fn + + batch_outputs = map_fn( + _single_image_nms_fn, + elems=([boxes, scores, masks, clip_window] + + list(ordered_additional_fields.values()) + [num_valid_boxes]), + dtype=(num_nmsed_outputs * [tf.float32] + [tf.int32]), + parallel_iterations=parallel_iterations) + + batch_nmsed_boxes = batch_outputs[0] + batch_nmsed_scores = batch_outputs[1] + batch_nmsed_classes = batch_outputs[2] + batch_nmsed_masks = batch_outputs[3] + batch_nmsed_values = batch_outputs[4:-1] + + batch_nmsed_additional_fields = {} + if num_additional_fields > 0: + # Sort the keys to ensure arranging elements in same order as + # in _single_image_nms_fn. + batch_nmsed_keys = list(ordered_additional_fields.keys()) + for i in range(len(batch_nmsed_keys)): + batch_nmsed_additional_fields[ + batch_nmsed_keys[i]] = batch_nmsed_values[i] + + batch_num_detections = batch_outputs[-1] + + if original_masks is None: + batch_nmsed_masks = None + + if not ordered_additional_fields: + batch_nmsed_additional_fields = None + + return (batch_nmsed_boxes, batch_nmsed_scores, batch_nmsed_classes, + batch_nmsed_masks, batch_nmsed_additional_fields, + batch_num_detections) diff --git a/workspace/virtuallab/object_detection/core/post_processing.pyc b/workspace/virtuallab/object_detection/core/post_processing.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e2708f0b034b471b7d6e5055589d6d3ba13b233 GIT binary patch literal 43919 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/prefetcher.py b/workspace/virtuallab/object_detection/core/prefetcher.py new file mode 100644 index 0000000..31e93ea --- /dev/null +++ b/workspace/virtuallab/object_detection/core/prefetcher.py @@ -0,0 +1,61 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Provides functions to prefetch tensors to feed into models.""" +import tensorflow.compat.v1 as tf + + +def prefetch(tensor_dict, capacity): + """Creates a prefetch queue for tensors. + + Creates a FIFO queue to asynchronously enqueue tensor_dicts and returns a + dequeue op that evaluates to a tensor_dict. This function is useful in + prefetching preprocessed tensors so that the data is readily available for + consumers. + + Example input pipeline when you don't need batching: + ---------------------------------------------------- + key, string_tensor = slim.parallel_reader.parallel_read(...) + tensor_dict = decoder.decode(string_tensor) + tensor_dict = preprocessor.preprocess(tensor_dict, ...) + prefetch_queue = prefetcher.prefetch(tensor_dict, capacity=20) + tensor_dict = prefetch_queue.dequeue() + outputs = Model(tensor_dict) + ... + ---------------------------------------------------- + + For input pipelines with batching, refer to core/batcher.py + + Args: + tensor_dict: a dictionary of tensors to prefetch. + capacity: the size of the prefetch queue. + + Returns: + a FIFO prefetcher queue + """ + names = list(tensor_dict.keys()) + dtypes = [t.dtype for t in tensor_dict.values()] + shapes = [t.get_shape() for t in tensor_dict.values()] + prefetch_queue = tf.PaddingFIFOQueue(capacity, dtypes=dtypes, + shapes=shapes, + names=names, + name='prefetch_queue') + enqueue_op = prefetch_queue.enqueue(tensor_dict) + tf.train.queue_runner.add_queue_runner(tf.train.queue_runner.QueueRunner( + prefetch_queue, [enqueue_op])) + tf.summary.scalar( + 'queue/%s/fraction_of_%d_full' % (prefetch_queue.name, capacity), + tf.cast(prefetch_queue.size(), dtype=tf.float32) * (1. / capacity)) + return prefetch_queue diff --git a/workspace/virtuallab/object_detection/core/prefetcher.pyc b/workspace/virtuallab/object_detection/core/prefetcher.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50c31e010de1d6b658351759cc2b7f0e4e550c93 GIT binary patch literal 2080 zcmb_d-HsbI6h4z|_OCy?tO`P?5*E^`LE0v?aJ8^N1zm{?prl+?f{}OZNhVCjQ`@sk zP;ynSc>vymx8QM*xa5L<$Ifhafvbv%#y(@8^PRuXPWR7SyTASNYnsvO>0tg8uRFw` zh@N3gNOh=8XxgD^m!55q>eA2H>hb#1{syamh~bGf=Y`Tv^a&^WGic;)l& z_GsCqMVDjyzC+L7rsc*zG!9yg4YpvVN3`sb@6mLV7Dz9l1+wbU0x1EjLkgcgc-vS) zVUwm?90EJv3Rc@R-J$9375N6MEw*TkFcDWm38UYn=`B(b;x>EAJM;!`>;=?)4o5}E z*I<;WZB}~y4j>ZXJ?-ys5q|HY8dgh>(#mx`pEtVEA~&|J>|l3Sh_&MB7pGr~sDwgG zH?OjZHI->xITyNG?S!VJs>nRRD&&cJ>Cib{-yvphp5Vl^^ZN;|@R7h}%kDrPQz z@SK-M`u899`Cq~_+!gwJUTm8-3OOXKF~UG27DrrN&;Kr~3{q3~@kL>9A3z#T%uElb z=4=@Mnug|VsxzM|?J [height, width, channels]. + original_minval: current image minimum value. + original_maxval: current image maximum value. + target_minval: target image minimum value. + target_maxval: target image maximum value. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('NormalizeImage', values=[image]): + original_minval = float(original_minval) + original_maxval = float(original_maxval) + target_minval = float(target_minval) + target_maxval = float(target_maxval) + image = tf.cast(image, dtype=tf.float32) + image = tf.subtract(image, original_minval) + image = tf.multiply(image, (target_maxval - target_minval) / + (original_maxval - original_minval)) + image = tf.add(image, target_minval) + return image + + +def retain_boxes_above_threshold(boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + threshold=0.0): + """Retains boxes whose label weight is above a given threshold. + + If the label weight for a box is missing (represented by NaN), the box is + retained. The boxes that don't pass the threshold will not appear in the + returned tensor. + + Args: + boxes: float32 tensor of shape [num_instance, 4] representing boxes + location in normalized coordinates. + labels: rank 1 int32 tensor of shape [num_instance] containing the object + classes. + label_weights: float32 tensor of shape [num_instance] representing the + weight for each box. + label_confidences: float32 tensor of shape [num_instance] representing the + confidence for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks are of + the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x normalized + coordinates. + threshold: scalar python float. + + Returns: + retained_boxes: [num_retained_instance, 4] + retianed_labels: [num_retained_instance] + retained_label_weights: [num_retained_instance] + + If multiclass_scores, masks, or keypoints are not None, the function also + returns: + + retained_multiclass_scores: [num_retained_instance, num_classes] + retained_masks: [num_retained_instance, height, width] + retained_keypoints: [num_retained_instance, num_keypoints, 2] + """ + with tf.name_scope('RetainBoxesAboveThreshold', + values=[boxes, labels, label_weights]): + indices = tf.where( + tf.logical_or(label_weights > threshold, tf.is_nan(label_weights))) + indices = tf.squeeze(indices, axis=1) + retained_boxes = tf.gather(boxes, indices) + retained_labels = tf.gather(labels, indices) + retained_label_weights = tf.gather(label_weights, indices) + result = [retained_boxes, retained_labels, retained_label_weights] + + if label_confidences is not None: + retained_label_confidences = tf.gather(label_confidences, indices) + result.append(retained_label_confidences) + + if multiclass_scores is not None: + retained_multiclass_scores = tf.gather(multiclass_scores, indices) + result.append(retained_multiclass_scores) + + if masks is not None: + retained_masks = tf.gather(masks, indices) + result.append(retained_masks) + + if keypoints is not None: + retained_keypoints = tf.gather(keypoints, indices) + result.append(retained_keypoints) + + return result + + +def drop_label_probabilistically(boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + dropped_label=None, + drop_probability=0.0, + seed=None): + """Drops boxes of a certain label with probability drop_probability. + + Boxes of the label dropped_label will not appear in the returned tensor. + + Args: + boxes: float32 tensor of shape [num_instance, 4] representing boxes + location in normalized coordinates. + labels: rank 1 int32 tensor of shape [num_instance] containing the object + classes. + label_weights: float32 tensor of shape [num_instance] representing the + weight for each box. + label_confidences: float32 tensor of shape [num_instance] representing the + confidence for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks are of + the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x normalized + coordinates. + dropped_label: int32 id of label to drop. + drop_probability: float32 probability of dropping a label. + seed: random seed. + + Returns: + retained_boxes: [num_retained_instance, 4] + retianed_labels: [num_retained_instance] + retained_label_weights: [num_retained_instance] + + If multiclass_scores, masks, or keypoints are not None, the function also + returns: + + retained_multiclass_scores: [num_retained_instance, num_classes] + retained_masks: [num_retained_instance, height, width] + retained_keypoints: [num_retained_instance, num_keypoints, 2] + """ + with tf.name_scope('DropLabelProbabilistically', + values=[boxes, labels]): + indices = tf.where( + tf.logical_or( + tf.random_uniform(tf.shape(labels), seed=seed) > drop_probability, + tf.not_equal(labels, dropped_label))) + indices = tf.squeeze(indices, axis=1) + + retained_boxes = tf.gather(boxes, indices) + retained_labels = tf.gather(labels, indices) + retained_label_weights = tf.gather(label_weights, indices) + result = [retained_boxes, retained_labels, retained_label_weights] + + if label_confidences is not None: + retained_label_confidences = tf.gather(label_confidences, indices) + result.append(retained_label_confidences) + + if multiclass_scores is not None: + retained_multiclass_scores = tf.gather(multiclass_scores, indices) + result.append(retained_multiclass_scores) + + if masks is not None: + retained_masks = tf.gather(masks, indices) + result.append(retained_masks) + + if keypoints is not None: + retained_keypoints = tf.gather(keypoints, indices) + result.append(retained_keypoints) + + return result + + +def remap_labels(labels, + original_labels=None, + new_label=None): + """Remaps labels that have an id in original_labels to new_label. + + Args: + labels: rank 1 int32 tensor of shape [num_instance] containing the object + classes. + original_labels: int list of original labels that should be mapped from. + new_label: int label to map to + Returns: + Remapped labels + """ + new_labels = labels + for original_label in original_labels: + change = tf.where( + tf.equal(new_labels, original_label), + tf.add(tf.zeros_like(new_labels), new_label - original_label), + tf.zeros_like(new_labels)) + new_labels = tf.add( + new_labels, + change) + new_labels = tf.reshape(new_labels, tf.shape(labels)) + return new_labels + + +def _flip_boxes_left_right(boxes): + """Left-right flip the boxes. + + Args: + boxes: Float32 tensor containing the bounding boxes -> [..., 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each last dimension is in the form of [ymin, xmin, ymax, xmax]. + + Returns: + Flipped boxes. + """ + ymin, xmin, ymax, xmax = tf.split(value=boxes, num_or_size_splits=4, axis=-1) + flipped_xmin = tf.subtract(1.0, xmax) + flipped_xmax = tf.subtract(1.0, xmin) + flipped_boxes = tf.concat([ymin, flipped_xmin, ymax, flipped_xmax], axis=-1) + return flipped_boxes + + +def _flip_boxes_up_down(boxes): + """Up-down flip the boxes. + + Args: + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + + Returns: + Flipped boxes. + """ + ymin, xmin, ymax, xmax = tf.split(value=boxes, num_or_size_splits=4, axis=1) + flipped_ymin = tf.subtract(1.0, ymax) + flipped_ymax = tf.subtract(1.0, ymin) + flipped_boxes = tf.concat([flipped_ymin, xmin, flipped_ymax, xmax], 1) + return flipped_boxes + + +def _rot90_boxes(boxes): + """Rotate boxes counter-clockwise by 90 degrees. + + Args: + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + + Returns: + Rotated boxes. + """ + ymin, xmin, ymax, xmax = tf.split(value=boxes, num_or_size_splits=4, axis=1) + rotated_ymin = tf.subtract(1.0, xmax) + rotated_ymax = tf.subtract(1.0, xmin) + rotated_xmin = ymin + rotated_xmax = ymax + rotated_boxes = tf.concat( + [rotated_ymin, rotated_xmin, rotated_ymax, rotated_xmax], 1) + return rotated_boxes + + +def _flip_masks_left_right(masks): + """Left-right flip masks. + + Args: + masks: rank 3 float32 tensor with shape + [num_instances, height, width] representing instance masks. + + Returns: + flipped masks: rank 3 float32 tensor with shape + [num_instances, height, width] representing instance masks. + """ + return masks[:, :, ::-1] + + +def _flip_masks_up_down(masks): + """Up-down flip masks. + + Args: + masks: rank 3 float32 tensor with shape + [num_instances, height, width] representing instance masks. + + Returns: + flipped masks: rank 3 float32 tensor with shape + [num_instances, height, width] representing instance masks. + """ + return masks[:, ::-1, :] + + +def _rot90_masks(masks): + """Rotate masks counter-clockwise by 90 degrees. + + Args: + masks: rank 3 float32 tensor with shape + [num_instances, height, width] representing instance masks. + + Returns: + rotated masks: rank 3 float32 tensor with shape + [num_instances, height, width] representing instance masks. + """ + masks = tf.transpose(masks, [0, 2, 1]) + return masks[:, ::-1, :] + + +def random_horizontal_flip(image, + boxes=None, + masks=None, + keypoints=None, + keypoint_visibilities=None, + densepose_part_ids=None, + densepose_surface_coords=None, + keypoint_flip_permutation=None, + probability=0.5, + seed=None, + preprocess_vars_cache=None): + """Randomly flips the image and detections horizontally. + + Args: + image: rank 3 float32 tensor with shape [height, width, channels]. + boxes: (optional) rank 2 float32 tensor with shape [N, 4] + containing the bounding boxes. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + keypoint_visibilities: (optional) rank 2 bool tensor with shape + [num_instances, num_keypoints]. + densepose_part_ids: (optional) rank 2 int32 tensor with shape + [num_instances, num_points] holding the part id for each + sampled point. These part_ids are 0-indexed, where the + first non-background part has index 0. + densepose_surface_coords: (optional) rank 3 float32 tensor with shape + [num_instances, num_points, 4]. The DensePose + coordinates are of the form (y, x, v, u) where + (y, x) are the normalized image coordinates for a + sampled point, and (v, u) is the surface + coordinate for the part. + keypoint_flip_permutation: rank 1 int32 tensor containing the keypoint flip + permutation. + probability: the probability of performing this augmentation. + seed: random seed + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + + If boxes, masks, keypoints, keypoint_visibilities, + keypoint_flip_permutation, densepose_part_ids, or densepose_surface_coords + are not None,the function also returns the following tensors. + + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + keypoint_visibilities: rank 2 bool tensor with shape + [num_instances, num_keypoints]. + densepose_part_ids: rank 2 int32 tensor with shape + [num_instances, num_points]. + densepose_surface_coords: rank 3 float32 tensor with shape + [num_instances, num_points, 4]. + + Raises: + ValueError: if keypoints are provided but keypoint_flip_permutation is not. + ValueError: if either densepose_part_ids or densepose_surface_coords is + not None, but both are not None. + """ + + def _flip_image(image): + # flip image + image_flipped = tf.image.flip_left_right(image) + return image_flipped + + if keypoints is not None and keypoint_flip_permutation is None: + raise ValueError( + 'keypoints are provided but keypoints_flip_permutation is not provided') + + if ((densepose_part_ids is not None and densepose_surface_coords is None) or + (densepose_part_ids is None and densepose_surface_coords is not None)): + raise ValueError( + 'Must provide both `densepose_part_ids` and `densepose_surface_coords`') + + with tf.name_scope('RandomHorizontalFlip', values=[image, boxes]): + result = [] + # random variable defining whether to do flip or not + generator_func = functools.partial(tf.random_uniform, [], seed=seed) + do_a_flip_random = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.HORIZONTAL_FLIP, + preprocess_vars_cache) + do_a_flip_random = tf.less(do_a_flip_random, probability) + + # flip image + image = tf.cond(do_a_flip_random, lambda: _flip_image(image), lambda: image) + result.append(image) + + # flip boxes + if boxes is not None: + boxes = tf.cond(do_a_flip_random, lambda: _flip_boxes_left_right(boxes), + lambda: boxes) + result.append(boxes) + + # flip masks + if masks is not None: + masks = tf.cond(do_a_flip_random, lambda: _flip_masks_left_right(masks), + lambda: masks) + result.append(masks) + + # flip keypoints + if keypoints is not None and keypoint_flip_permutation is not None: + permutation = keypoint_flip_permutation + keypoints = tf.cond( + do_a_flip_random, + lambda: keypoint_ops.flip_horizontal(keypoints, 0.5, permutation), + lambda: keypoints) + result.append(keypoints) + + # flip keypoint visibilities + if (keypoint_visibilities is not None and + keypoint_flip_permutation is not None): + kpt_flip_perm = keypoint_flip_permutation + keypoint_visibilities = tf.cond( + do_a_flip_random, + lambda: tf.gather(keypoint_visibilities, kpt_flip_perm, axis=1), + lambda: keypoint_visibilities) + result.append(keypoint_visibilities) + + # flip DensePose parts and coordinates + if densepose_part_ids is not None: + flip_densepose_fn = functools.partial( + densepose_ops.flip_horizontal, densepose_part_ids, + densepose_surface_coords) + densepose_tensors = tf.cond( + do_a_flip_random, + flip_densepose_fn, + lambda: (densepose_part_ids, densepose_surface_coords)) + result.extend(densepose_tensors) + + return tuple(result) + + +def random_vertical_flip(image, + boxes=None, + masks=None, + keypoints=None, + keypoint_flip_permutation=None, + probability=0.5, + seed=None, + preprocess_vars_cache=None): + """Randomly flips the image and detections vertically. + + The probability of flipping the image is 50%. + + Args: + image: rank 3 float32 tensor with shape [height, width, channels]. + boxes: (optional) rank 2 float32 tensor with shape [N, 4] + containing the bounding boxes. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + keypoint_flip_permutation: rank 1 int32 tensor containing the keypoint flip + permutation. + probability: the probability of performing this augmentation. + seed: random seed + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + + If boxes, masks, keypoints, and keypoint_flip_permutation are not None, + the function also returns the following tensors. + + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + + Raises: + ValueError: if keypoints are provided but keypoint_flip_permutation is not. + """ + + def _flip_image(image): + # flip image + image_flipped = tf.image.flip_up_down(image) + return image_flipped + + if keypoints is not None and keypoint_flip_permutation is None: + raise ValueError( + 'keypoints are provided but keypoints_flip_permutation is not provided') + + with tf.name_scope('RandomVerticalFlip', values=[image, boxes]): + result = [] + # random variable defining whether to do flip or not + generator_func = functools.partial(tf.random_uniform, [], seed=seed) + do_a_flip_random = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.VERTICAL_FLIP, + preprocess_vars_cache) + do_a_flip_random = tf.less(do_a_flip_random, probability) + + # flip image + image = tf.cond(do_a_flip_random, lambda: _flip_image(image), lambda: image) + result.append(image) + + # flip boxes + if boxes is not None: + boxes = tf.cond(do_a_flip_random, lambda: _flip_boxes_up_down(boxes), + lambda: boxes) + result.append(boxes) + + # flip masks + if masks is not None: + masks = tf.cond(do_a_flip_random, lambda: _flip_masks_up_down(masks), + lambda: masks) + result.append(masks) + + # flip keypoints + if keypoints is not None and keypoint_flip_permutation is not None: + permutation = keypoint_flip_permutation + keypoints = tf.cond( + do_a_flip_random, + lambda: keypoint_ops.flip_vertical(keypoints, 0.5, permutation), + lambda: keypoints) + result.append(keypoints) + + return tuple(result) + + +def random_rotation90(image, + boxes=None, + masks=None, + keypoints=None, + keypoint_rot_permutation=None, + probability=0.5, + seed=None, + preprocess_vars_cache=None): + """Randomly rotates the image and detections 90 degrees counter-clockwise. + + The probability of rotating the image is 50%. This can be combined with + random_horizontal_flip and random_vertical_flip to produce an output with a + uniform distribution of the eight possible 90 degree rotation / reflection + combinations. + + Args: + image: rank 3 float32 tensor with shape [height, width, channels]. + boxes: (optional) rank 2 float32 tensor with shape [N, 4] + containing the bounding boxes. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + keypoint_rot_permutation: rank 1 int32 tensor containing the keypoint flip + permutation. + probability: the probability of performing this augmentation. + seed: random seed + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + + If boxes, masks, and keypoints, are not None, + the function also returns the following tensors. + + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + """ + + def _rot90_image(image): + # flip image + image_rotated = tf.image.rot90(image) + return image_rotated + + with tf.name_scope('RandomRotation90', values=[image, boxes]): + result = [] + + # random variable defining whether to rotate by 90 degrees or not + generator_func = functools.partial(tf.random_uniform, [], seed=seed) + do_a_rot90_random = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.ROTATION90, + preprocess_vars_cache) + do_a_rot90_random = tf.less(do_a_rot90_random, probability) + + # flip image + image = tf.cond(do_a_rot90_random, lambda: _rot90_image(image), + lambda: image) + result.append(image) + + # flip boxes + if boxes is not None: + boxes = tf.cond(do_a_rot90_random, lambda: _rot90_boxes(boxes), + lambda: boxes) + result.append(boxes) + + # flip masks + if masks is not None: + masks = tf.cond(do_a_rot90_random, lambda: _rot90_masks(masks), + lambda: masks) + result.append(masks) + + # flip keypoints + if keypoints is not None: + keypoints = tf.cond( + do_a_rot90_random, + lambda: keypoint_ops.rot90(keypoints, keypoint_rot_permutation), + lambda: keypoints) + result.append(keypoints) + + return tuple(result) + + +def random_pixel_value_scale(image, + minval=0.9, + maxval=1.1, + seed=None, + preprocess_vars_cache=None): + """Scales each value in the pixels of the image. + + This function scales each pixel independent of the other ones. + For each value in image tensor, draws a random number between + minval and maxval and multiples the values with them. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + minval: lower ratio of scaling pixel values. + maxval: upper ratio of scaling pixel values. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomPixelValueScale', values=[image]): + generator_func = functools.partial( + tf.random_uniform, tf.shape(image), + minval=minval, maxval=maxval, + dtype=tf.float32, seed=seed) + color_coef = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.PIXEL_VALUE_SCALE, + preprocess_vars_cache) + + image = tf.multiply(image, color_coef) + image = tf.clip_by_value(image, 0.0, 255.0) + + return image + + +def random_image_scale(image, + masks=None, + min_scale_ratio=0.5, + max_scale_ratio=2.0, + seed=None, + preprocess_vars_cache=None): + """Scales the image size. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels]. + masks: (optional) rank 3 float32 tensor containing masks with + size [height, width, num_masks]. The value is set to None if there are no + masks. + min_scale_ratio: minimum scaling ratio. + max_scale_ratio: maximum scaling ratio. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + masks: If masks is not none, resized masks which are the same rank as input + masks will be returned. + """ + with tf.name_scope('RandomImageScale', values=[image]): + result = [] + image_shape = tf.shape(image) + image_height = image_shape[0] + image_width = image_shape[1] + generator_func = functools.partial( + tf.random_uniform, [], + minval=min_scale_ratio, maxval=max_scale_ratio, + dtype=tf.float32, seed=seed) + size_coef = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.IMAGE_SCALE, + preprocess_vars_cache) + + image_newysize = tf.cast( + tf.multiply(tf.cast(image_height, dtype=tf.float32), size_coef), + dtype=tf.int32) + image_newxsize = tf.cast( + tf.multiply(tf.cast(image_width, dtype=tf.float32), size_coef), + dtype=tf.int32) + image = tf.image.resize_images( + image, [image_newysize, image_newxsize], align_corners=True) + result.append(image) + if masks is not None: + masks = tf.image.resize_images( + masks, [image_newysize, image_newxsize], + method=tf.image.ResizeMethod.NEAREST_NEIGHBOR, + align_corners=True) + result.append(masks) + return tuple(result) + + +def _augment_only_rgb_channels(image, augment_function): + """Augments only the RGB slice of an image with additional channels.""" + rgb_slice = image[:, :, :3] + augmented_rgb_slice = augment_function(rgb_slice) + image = tf.concat([augmented_rgb_slice, image[:, :, 3:]], -1) + return image + + +def random_rgb_to_gray(image, + probability=0.1, + seed=None, + preprocess_vars_cache=None): + """Changes the image from RGB to Grayscale with the given probability. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + probability: the probability of returning a grayscale image. + The probability should be a number between [0, 1]. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + """ + def _image_to_gray(image): + image_gray1 = _rgb_to_grayscale(image) + image_gray3 = tf.image.grayscale_to_rgb(image_gray1) + return image_gray3 + + with tf.name_scope('RandomRGBtoGray', values=[image]): + # random variable defining whether to change to grayscale or not + generator_func = functools.partial(tf.random_uniform, [], seed=seed) + do_gray_random = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.RGB_TO_GRAY, + preprocess_vars_cache) + + image = tf.cond( + tf.greater(do_gray_random, probability), lambda: image, + lambda: _augment_only_rgb_channels(image, _image_to_gray)) + + return image + + +def random_adjust_brightness(image, + max_delta=0.2, + seed=None, + preprocess_vars_cache=None): + """Randomly adjusts brightness. + + Makes sure the output image is still between 0 and 255. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + max_delta: how much to change the brightness. A value between [0, 1). + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + boxes: boxes which is the same shape as input boxes. + """ + with tf.name_scope('RandomAdjustBrightness', values=[image]): + generator_func = functools.partial(tf.random_uniform, [], + -max_delta, max_delta, seed=seed) + delta = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.ADJUST_BRIGHTNESS, + preprocess_vars_cache) + + def _adjust_brightness(image): + image = tf.image.adjust_brightness(image / 255, delta) * 255 + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=255.0) + return image + + image = _augment_only_rgb_channels(image, _adjust_brightness) + return image + + +def random_adjust_contrast(image, + min_delta=0.8, + max_delta=1.25, + seed=None, + preprocess_vars_cache=None): + """Randomly adjusts contrast. + + Makes sure the output image is still between 0 and 255. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + min_delta: see max_delta. + max_delta: how much to change the contrast. Contrast will change with a + value between min_delta and max_delta. This value will be + multiplied to the current contrast of the image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomAdjustContrast', values=[image]): + generator_func = functools.partial(tf.random_uniform, [], + min_delta, max_delta, seed=seed) + contrast_factor = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.ADJUST_CONTRAST, + preprocess_vars_cache) + + def _adjust_contrast(image): + image = tf.image.adjust_contrast(image / 255, contrast_factor) * 255 + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=255.0) + return image + image = _augment_only_rgb_channels(image, _adjust_contrast) + return image + + +def random_adjust_hue(image, + max_delta=0.02, + seed=None, + preprocess_vars_cache=None): + """Randomly adjusts hue. + + Makes sure the output image is still between 0 and 255. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + max_delta: change hue randomly with a value between 0 and max_delta. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomAdjustHue', values=[image]): + generator_func = functools.partial(tf.random_uniform, [], + -max_delta, max_delta, seed=seed) + delta = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.ADJUST_HUE, + preprocess_vars_cache) + def _adjust_hue(image): + image = tf.image.adjust_hue(image / 255, delta) * 255 + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=255.0) + return image + image = _augment_only_rgb_channels(image, _adjust_hue) + return image + + +def random_adjust_saturation(image, + min_delta=0.8, + max_delta=1.25, + seed=None, + preprocess_vars_cache=None): + """Randomly adjusts saturation. + + Makes sure the output image is still between 0 and 255. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + min_delta: see max_delta. + max_delta: how much to change the saturation. Saturation will change with a + value between min_delta and max_delta. This value will be + multiplied to the current saturation of the image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + """ + with tf.name_scope('RandomAdjustSaturation', values=[image]): + generator_func = functools.partial(tf.random_uniform, [], + min_delta, max_delta, seed=seed) + saturation_factor = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.ADJUST_SATURATION, + preprocess_vars_cache) + def _adjust_saturation(image): + image = tf.image.adjust_saturation(image / 255, saturation_factor) * 255 + image = tf.clip_by_value(image, clip_value_min=0.0, clip_value_max=255.0) + return image + image = _augment_only_rgb_channels(image, _adjust_saturation) + return image + + +def random_distort_color(image, color_ordering=0, preprocess_vars_cache=None): + """Randomly distorts color. + + Randomly distorts color using a combination of brightness, hue, contrast and + saturation changes. Makes sure the output image is still between 0 and 255. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + color_ordering: Python int, a type of distortion (valid values: 0, 1). + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same shape as input image. + + Raises: + ValueError: if color_ordering is not in {0, 1}. + """ + with tf.name_scope('RandomDistortColor', values=[image]): + if color_ordering == 0: + image = random_adjust_brightness( + image, max_delta=32. / 255., + preprocess_vars_cache=preprocess_vars_cache) + image = random_adjust_saturation( + image, min_delta=0.5, max_delta=1.5, + preprocess_vars_cache=preprocess_vars_cache) + image = random_adjust_hue( + image, max_delta=0.2, + preprocess_vars_cache=preprocess_vars_cache) + image = random_adjust_contrast( + image, min_delta=0.5, max_delta=1.5, + preprocess_vars_cache=preprocess_vars_cache) + + elif color_ordering == 1: + image = random_adjust_brightness( + image, max_delta=32. / 255., + preprocess_vars_cache=preprocess_vars_cache) + image = random_adjust_contrast( + image, min_delta=0.5, max_delta=1.5, + preprocess_vars_cache=preprocess_vars_cache) + image = random_adjust_saturation( + image, min_delta=0.5, max_delta=1.5, + preprocess_vars_cache=preprocess_vars_cache) + image = random_adjust_hue( + image, max_delta=0.2, + preprocess_vars_cache=preprocess_vars_cache) + else: + raise ValueError('color_ordering must be in {0, 1}') + return image + + +def random_jitter_boxes(boxes, ratio=0.05, seed=None): + """Randomly jitter boxes in image. + + Args: + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + ratio: The ratio of the box width and height that the corners can jitter. + For example if the width is 100 pixels and ratio is 0.05, + the corners can jitter up to 5 pixels in the x direction. + seed: random seed. + + Returns: + boxes: boxes which is the same shape as input boxes. + """ + def random_jitter_box(box, ratio, seed): + """Randomly jitter box. + + Args: + box: bounding box [1, 1, 4]. + ratio: max ratio between jittered box and original box, + a number between [0, 0.5]. + seed: random seed. + + Returns: + jittered_box: jittered box. + """ + rand_numbers = tf.random_uniform( + [1, 1, 4], minval=-ratio, maxval=ratio, dtype=tf.float32, seed=seed) + box_width = tf.subtract(box[0, 0, 3], box[0, 0, 1]) + box_height = tf.subtract(box[0, 0, 2], box[0, 0, 0]) + hw_coefs = tf.stack([box_height, box_width, box_height, box_width]) + hw_rand_coefs = tf.multiply(hw_coefs, rand_numbers) + jittered_box = tf.add(box, hw_rand_coefs) + jittered_box = tf.clip_by_value(jittered_box, 0.0, 1.0) + return jittered_box + + with tf.name_scope('RandomJitterBoxes', values=[boxes]): + # boxes are [N, 4]. Lets first make them [N, 1, 1, 4] + boxes_shape = tf.shape(boxes) + boxes = tf.expand_dims(boxes, 1) + boxes = tf.expand_dims(boxes, 2) + + distorted_boxes = tf.map_fn( + lambda x: random_jitter_box(x, ratio, seed), boxes, dtype=tf.float32) + + distorted_boxes = tf.reshape(distorted_boxes, boxes_shape) + + return distorted_boxes + + +def _strict_random_crop_image(image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + keypoint_visibilities=None, + densepose_num_points=None, + densepose_part_ids=None, + densepose_surface_coords=None, + min_object_covered=1.0, + aspect_ratio_range=(0.75, 1.33), + area_range=(0.1, 1.0), + overlap_thresh=0.3, + clip_boxes=True, + preprocess_vars_cache=None): + """Performs random crop. + + Note: Keypoint coordinates that are outside the crop will be set to NaN, which + is consistent with the original keypoint encoding for non-existing keypoints. + This function always crops the image and is supposed to be used by + `random_crop_image` function which sometimes returns the image unchanged. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes with shape + [num_instances, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: (optional) float32 tensor of shape [num_instances] + representing the confidence for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + keypoint_visibilities: (optional) rank 2 bool tensor with shape + [num_instances, num_keypoints]. + densepose_num_points: (optional) rank 1 int32 tensor with shape + [num_instances] with the number of sampled points per + instance. + densepose_part_ids: (optional) rank 2 int32 tensor with shape + [num_instances, num_points] holding the part id for each + sampled point. These part_ids are 0-indexed, where the + first non-background part has index 0. + densepose_surface_coords: (optional) rank 3 float32 tensor with shape + [num_instances, num_points, 4]. The DensePose + coordinates are of the form (y, x, v, u) where + (y, x) are the normalized image coordinates for a + sampled point, and (v, u) is the surface + coordinate for the part. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio_range: allowed range for aspect ratio of cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + + If label_weights, multiclass_scores, masks, keypoints, + keypoint_visibilities, densepose_num_points, densepose_part_ids, or + densepose_surface_coords is not None, the function also returns: + label_weights: rank 1 float32 tensor with shape [num_instances]. + multiclass_scores: rank 2 float32 tensor with shape + [num_instances, num_classes] + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + keypoint_visibilities: rank 2 bool tensor with shape + [num_instances, num_keypoints] + densepose_num_points: rank 1 int32 tensor with shape [num_instances]. + densepose_part_ids: rank 2 int32 tensor with shape + [num_instances, num_points]. + densepose_surface_coords: rank 3 float32 tensor with shape + [num_instances, num_points, 4]. + + Raises: + ValueError: If some but not all of the DensePose tensors are provided. + """ + with tf.name_scope('RandomCropImage', values=[image, boxes]): + densepose_tensors = [densepose_num_points, densepose_part_ids, + densepose_surface_coords] + if (any(t is not None for t in densepose_tensors) and + not all(t is not None for t in densepose_tensors)): + raise ValueError('If cropping DensePose labels, must provide ' + '`densepose_num_points`, `densepose_part_ids`, and ' + '`densepose_surface_coords`') + image_shape = tf.shape(image) + + # boxes are [N, 4]. Lets first make them [N, 1, 4]. + boxes_expanded = tf.expand_dims( + tf.clip_by_value( + boxes, clip_value_min=0.0, clip_value_max=1.0), 1) + + generator_func = functools.partial( + tf.image.sample_distorted_bounding_box, + image_shape, + bounding_boxes=boxes_expanded, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + max_attempts=100, + use_image_if_no_bounding_boxes=True) + + # for ssd cropping, each value of min_object_covered has its own + # cached random variable + sample_distorted_bounding_box = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.STRICT_CROP_IMAGE, + preprocess_vars_cache, key=min_object_covered) + + im_box_begin, im_box_size, im_box = sample_distorted_bounding_box + im_box_end = im_box_begin + im_box_size + new_image = image[im_box_begin[0]:im_box_end[0], + im_box_begin[1]:im_box_end[1], :] + new_image.set_shape([None, None, image.get_shape()[2]]) + + # [1, 4] + im_box_rank2 = tf.squeeze(im_box, axis=[0]) + # [4] + im_box_rank1 = tf.squeeze(im_box) + + boxlist = box_list.BoxList(boxes) + boxlist.add_field('labels', labels) + + if label_weights is not None: + boxlist.add_field('label_weights', label_weights) + + if label_confidences is not None: + boxlist.add_field('label_confidences', label_confidences) + + if multiclass_scores is not None: + boxlist.add_field('multiclass_scores', multiclass_scores) + + im_boxlist = box_list.BoxList(im_box_rank2) + + # remove boxes that are outside cropped image + boxlist, inside_window_ids = box_list_ops.prune_completely_outside_window( + boxlist, im_box_rank1) + + # remove boxes that are outside image + overlapping_boxlist, keep_ids = box_list_ops.prune_non_overlapping_boxes( + boxlist, im_boxlist, overlap_thresh) + + # change the coordinate of the remaining boxes + new_labels = overlapping_boxlist.get_field('labels') + new_boxlist = box_list_ops.change_coordinate_frame(overlapping_boxlist, + im_box_rank1) + new_boxes = new_boxlist.get() + if clip_boxes: + new_boxes = tf.clip_by_value( + new_boxes, clip_value_min=0.0, clip_value_max=1.0) + + result = [new_image, new_boxes, new_labels] + + if label_weights is not None: + new_label_weights = overlapping_boxlist.get_field('label_weights') + result.append(new_label_weights) + + if label_confidences is not None: + new_label_confidences = overlapping_boxlist.get_field('label_confidences') + result.append(new_label_confidences) + + if multiclass_scores is not None: + new_multiclass_scores = overlapping_boxlist.get_field('multiclass_scores') + result.append(new_multiclass_scores) + + if masks is not None: + masks_of_boxes_inside_window = tf.gather(masks, inside_window_ids) + masks_of_boxes_completely_inside_window = tf.gather( + masks_of_boxes_inside_window, keep_ids) + new_masks = masks_of_boxes_completely_inside_window[:, im_box_begin[ + 0]:im_box_end[0], im_box_begin[1]:im_box_end[1]] + result.append(new_masks) + + if keypoints is not None: + keypoints_of_boxes_inside_window = tf.gather(keypoints, inside_window_ids) + keypoints_of_boxes_completely_inside_window = tf.gather( + keypoints_of_boxes_inside_window, keep_ids) + new_keypoints = keypoint_ops.change_coordinate_frame( + keypoints_of_boxes_completely_inside_window, im_box_rank1) + if clip_boxes: + new_keypoints = keypoint_ops.prune_outside_window(new_keypoints, + [0.0, 0.0, 1.0, 1.0]) + result.append(new_keypoints) + + if keypoint_visibilities is not None: + kpt_vis_of_boxes_inside_window = tf.gather(keypoint_visibilities, + inside_window_ids) + kpt_vis_of_boxes_completely_inside_window = tf.gather( + kpt_vis_of_boxes_inside_window, keep_ids) + if clip_boxes: + # Set any keypoints with NaN coordinates to invisible. + new_kpt_visibilities = keypoint_ops.set_keypoint_visibilities( + new_keypoints, kpt_vis_of_boxes_completely_inside_window) + result.append(new_kpt_visibilities) + + if densepose_num_points is not None: + filtered_dp_tensors = [] + for dp_tensor in densepose_tensors: + dp_tensor_inside_window = tf.gather(dp_tensor, inside_window_ids) + dp_tensor_completely_inside_window = tf.gather(dp_tensor_inside_window, + keep_ids) + filtered_dp_tensors.append(dp_tensor_completely_inside_window) + new_dp_num_points = filtered_dp_tensors[0] + new_dp_point_ids = filtered_dp_tensors[1] + new_dp_surf_coords = densepose_ops.change_coordinate_frame( + filtered_dp_tensors[2], im_box_rank1) + if clip_boxes: + new_dp_num_points, new_dp_point_ids, new_dp_surf_coords = ( + densepose_ops.prune_outside_window( + new_dp_num_points, new_dp_point_ids, new_dp_surf_coords, + window=[0.0, 0.0, 1.0, 1.0])) + result.extend([new_dp_num_points, new_dp_point_ids, new_dp_surf_coords]) + return tuple(result) + + +def random_crop_image(image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + keypoint_visibilities=None, + densepose_num_points=None, + densepose_part_ids=None, + densepose_surface_coords=None, + min_object_covered=1.0, + aspect_ratio_range=(0.75, 1.33), + area_range=(0.1, 1.0), + overlap_thresh=0.3, + clip_boxes=True, + random_coef=0.0, + seed=None, + preprocess_vars_cache=None): + """Randomly crops the image. + + Given the input image and its bounding boxes, this op randomly + crops a subimage. Given a user-provided set of input constraints, + the crop window is resampled until it satisfies these constraints. + If within 100 trials it is unable to find a valid crop, the original + image is returned. See the Args section for a description of the input + constraints. Both input boxes and returned Boxes are in normalized + form (e.g., lie in the unit square [0, 1]). + This function will return the original image with probability random_coef. + + Note: Keypoint coordinates that are outside the crop will be set to NaN, which + is consistent with the original keypoint encoding for non-existing keypoints. + Also, the keypoint visibility will be set to False. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes with shape + [num_instances, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: (optional) float32 tensor of shape [num_instances]. + representing the confidence for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + keypoint_visibilities: (optional) rank 2 bool tensor with shape + [num_instances, num_keypoints]. + densepose_num_points: (optional) rank 1 int32 tensor with shape + [num_instances] with the number of sampled points per + instance. + densepose_part_ids: (optional) rank 2 int32 tensor with shape + [num_instances, num_points] holding the part id for each + sampled point. These part_ids are 0-indexed, where the + first non-background part has index 0. + densepose_surface_coords: (optional) rank 3 float32 tensor with shape + [num_instances, num_points, 4]. The DensePose + coordinates are of the form (y, x, v, u) where + (y, x) are the normalized image coordinates for a + sampled point, and (v, u) is the surface + coordinate for the part. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio_range: allowed range for aspect ratio of cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the + cropped image, and if it is 1.0, we will always get the + original image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: Image shape will be [new_height, new_width, channels]. + boxes: boxes which is the same rank as input boxes. Boxes are in normalized + form. + labels: new labels. + + If label_weights, multiclass_scores, masks, keypoints, + keypoint_visibilities, densepose_num_points, densepose_part_ids, + densepose_surface_coords is not None, the function also returns: + label_weights: rank 1 float32 tensor with shape [num_instances]. + multiclass_scores: rank 2 float32 tensor with shape + [num_instances, num_classes] + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + keypoint_visibilities: rank 2 bool tensor with shape + [num_instances, num_keypoints] + densepose_num_points: rank 1 int32 tensor with shape [num_instances]. + densepose_part_ids: rank 2 int32 tensor with shape + [num_instances, num_points]. + densepose_surface_coords: rank 3 float32 tensor with shape + [num_instances, num_points, 4]. + """ + + def strict_random_crop_image_fn(): + return _strict_random_crop_image( + image, + boxes, + labels, + label_weights, + label_confidences=label_confidences, + multiclass_scores=multiclass_scores, + masks=masks, + keypoints=keypoints, + keypoint_visibilities=keypoint_visibilities, + densepose_num_points=densepose_num_points, + densepose_part_ids=densepose_part_ids, + densepose_surface_coords=densepose_surface_coords, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + overlap_thresh=overlap_thresh, + clip_boxes=clip_boxes, + preprocess_vars_cache=preprocess_vars_cache) + + # avoids tf.cond to make faster RCNN training on borg. See b/140057645. + if random_coef < sys.float_info.min: + result = strict_random_crop_image_fn() + else: + generator_func = functools.partial(tf.random_uniform, [], seed=seed) + do_a_crop_random = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.CROP_IMAGE, + preprocess_vars_cache) + do_a_crop_random = tf.greater(do_a_crop_random, random_coef) + + outputs = [image, boxes, labels] + + if label_weights is not None: + outputs.append(label_weights) + if label_confidences is not None: + outputs.append(label_confidences) + if multiclass_scores is not None: + outputs.append(multiclass_scores) + if masks is not None: + outputs.append(masks) + if keypoints is not None: + outputs.append(keypoints) + if keypoint_visibilities is not None: + outputs.append(keypoint_visibilities) + if densepose_num_points is not None: + outputs.extend([densepose_num_points, densepose_part_ids, + densepose_surface_coords]) + + result = tf.cond(do_a_crop_random, strict_random_crop_image_fn, + lambda: tuple(outputs)) + return result + + +def random_pad_image(image, + boxes, + masks=None, + keypoints=None, + densepose_surface_coords=None, + min_image_size=None, + max_image_size=None, + pad_color=None, + seed=None, + preprocess_vars_cache=None): + """Randomly pads the image. + + This function randomly pads the image with zeros. The final size of the + padded image will be between min_image_size and max_image_size. + if min_image_size is smaller than the input image size, min_image_size will + be set to the input image size. The same for max_image_size. The input image + will be located at a uniformly random location inside the padded image. + The relative location of the boxes to the original image will remain the same. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + masks: (optional) rank 3 float32 tensor with shape + [N, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [N, num_keypoints, 2]. The keypoints are in y-x normalized + coordinates. + densepose_surface_coords: (optional) rank 3 float32 tensor with shape + [N, num_points, 4]. The DensePose coordinates are + of the form (y, x, v, u) where (y, x) are the + normalized image coordinates for a sampled point, + and (v, u) is the surface coordinate for the part. + min_image_size: a tensor of size [min_height, min_width], type tf.int32. + If passed as None, will be set to image size + [height, width]. + max_image_size: a tensor of size [max_height, max_width], type tf.int32. + If passed as None, will be set to twice the + image [height * 2, width * 2]. + pad_color: padding color. A rank 1 tensor of [channels] with dtype= + tf.float32. if set as None, it will be set to average color of + the input image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: Image shape will be [new_height, new_width, channels]. + boxes: boxes which is the same rank as input boxes. Boxes are in normalized + form. + + if masks is not None, the function also returns: + masks: rank 3 float32 tensor with shape [N, new_height, new_width] + if keypoints is not None, the function also returns: + keypoints: rank 3 float32 tensor with shape [N, num_keypoints, 2] + if densepose_surface_coords is not None, the function also returns: + densepose_surface_coords: rank 3 float32 tensor with shape + [num_instances, num_points, 4] + """ + if pad_color is None: + pad_color = tf.reduce_mean(image, axis=[0, 1]) + + image_shape = tf.shape(image) + image_height = image_shape[0] + image_width = image_shape[1] + + if max_image_size is None: + max_image_size = tf.stack([image_height * 2, image_width * 2]) + max_image_size = tf.maximum(max_image_size, + tf.stack([image_height, image_width])) + + if min_image_size is None: + min_image_size = tf.stack([image_height, image_width]) + min_image_size = tf.maximum(min_image_size, + tf.stack([image_height, image_width])) + + target_height = tf.cond( + max_image_size[0] > min_image_size[0], + lambda: _random_integer(min_image_size[0], max_image_size[0], seed), + lambda: max_image_size[0]) + + target_width = tf.cond( + max_image_size[1] > min_image_size[1], + lambda: _random_integer(min_image_size[1], max_image_size[1], seed), + lambda: max_image_size[1]) + + offset_height = tf.cond( + target_height > image_height, + lambda: _random_integer(0, target_height - image_height, seed), + lambda: tf.constant(0, dtype=tf.int32)) + + offset_width = tf.cond( + target_width > image_width, + lambda: _random_integer(0, target_width - image_width, seed), + lambda: tf.constant(0, dtype=tf.int32)) + + gen_func = lambda: (target_height, target_width, offset_height, offset_width) + params = _get_or_create_preprocess_rand_vars( + gen_func, preprocessor_cache.PreprocessorCache.PAD_IMAGE, + preprocess_vars_cache) + target_height, target_width, offset_height, offset_width = params + + new_image = tf.image.pad_to_bounding_box( + image, + offset_height=offset_height, + offset_width=offset_width, + target_height=target_height, + target_width=target_width) + + # Setting color of the padded pixels + image_ones = tf.ones_like(image) + image_ones_padded = tf.image.pad_to_bounding_box( + image_ones, + offset_height=offset_height, + offset_width=offset_width, + target_height=target_height, + target_width=target_width) + image_color_padded = (1.0 - image_ones_padded) * pad_color + new_image += image_color_padded + + # setting boxes + new_window = tf.cast( + tf.stack([ + -offset_height, -offset_width, target_height - offset_height, + target_width - offset_width + ]), + dtype=tf.float32) + new_window /= tf.cast( + tf.stack([image_height, image_width, image_height, image_width]), + dtype=tf.float32) + boxlist = box_list.BoxList(boxes) + new_boxlist = box_list_ops.change_coordinate_frame(boxlist, new_window) + new_boxes = new_boxlist.get() + + result = [new_image, new_boxes] + + if masks is not None: + new_masks = tf.image.pad_to_bounding_box( + masks[:, :, :, tf.newaxis], + offset_height=offset_height, + offset_width=offset_width, + target_height=target_height, + target_width=target_width)[:, :, :, 0] + result.append(new_masks) + + if keypoints is not None: + new_keypoints = keypoint_ops.change_coordinate_frame(keypoints, new_window) + result.append(new_keypoints) + + if densepose_surface_coords is not None: + new_densepose_surface_coords = densepose_ops.change_coordinate_frame( + densepose_surface_coords, new_window) + result.append(new_densepose_surface_coords) + + return tuple(result) + + +def random_absolute_pad_image(image, + boxes, + masks=None, + keypoints=None, + densepose_surface_coords=None, + max_height_padding=None, + max_width_padding=None, + pad_color=None, + seed=None, + preprocess_vars_cache=None): + """Randomly pads the image by small absolute amounts. + + As random_pad_image above, but the padding is of size [0, max_height_padding] + or [0, max_width_padding] instead of padding to a fixed size of + max_height_padding for all images. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + masks: (optional) rank 3 float32 tensor with shape + [N, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [N, num_keypoints, 2]. The keypoints are in y-x normalized + coordinates. + densepose_surface_coords: (optional) rank 3 float32 tensor with shape + [N, num_points, 4]. The DensePose coordinates are + of the form (y, x, v, u) where (y, x) are the + normalized image coordinates for a sampled point, + and (v, u) is the surface coordinate for the part. + max_height_padding: a scalar tf.int32 tensor denoting the maximum amount of + height padding. The padding will be chosen uniformly at + random from [0, max_height_padding). + max_width_padding: a scalar tf.int32 tensor denoting the maximum amount of + width padding. The padding will be chosen uniformly at + random from [0, max_width_padding). + pad_color: padding color. A rank 1 tensor of [3] with dtype=tf.float32. + if set as None, it will be set to average color of the input + image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: Image shape will be [new_height, new_width, channels]. + boxes: boxes which is the same rank as input boxes. Boxes are in normalized + form. + if masks is not None, the function also returns: + masks: rank 3 float32 tensor with shape [N, new_height, new_width] + if keypoints is not None, the function also returns: + keypoints: rank 3 float32 tensor with shape [N, num_keypoints, 2] + """ + min_image_size = tf.shape(image)[:2] + max_image_size = min_image_size + tf.cast( + [max_height_padding, max_width_padding], dtype=tf.int32) + return random_pad_image( + image, + boxes, + masks=masks, + keypoints=keypoints, + densepose_surface_coords=densepose_surface_coords, + min_image_size=min_image_size, + max_image_size=max_image_size, + pad_color=pad_color, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + +def random_crop_pad_image(image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + min_object_covered=1.0, + aspect_ratio_range=(0.75, 1.33), + area_range=(0.1, 1.0), + overlap_thresh=0.3, + clip_boxes=True, + random_coef=0.0, + min_padded_size_ratio=(1.0, 1.0), + max_padded_size_ratio=(2.0, 2.0), + pad_color=None, + seed=None, + preprocess_vars_cache=None): + """Randomly crops and pads the image. + + Given an input image and its bounding boxes, this op first randomly crops + the image and then randomly pads the image with background values. Parameters + min_padded_size_ratio and max_padded_size_ratio, determine the range of the + final output image size. Specifically, the final image size will have a size + in the range of min_padded_size_ratio * tf.shape(image) and + max_padded_size_ratio * tf.shape(image). Note that these ratios are with + respect to the size of the original image, so we can't capture the same + effect easily by independently applying RandomCropImage + followed by RandomPadImage. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: rank 1 float32 containing the label weights. + label_confidences: rank 1 float32 containing the label confidences. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio_range: allowed range for aspect ratio of cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the + cropped image, and if it is 1.0, we will always get the + original image. + min_padded_size_ratio: min ratio of padded image height and width to the + input image's height and width. + max_padded_size_ratio: max ratio of padded image height and width to the + input image's height and width. + pad_color: padding color. A rank 1 tensor of [3] with dtype=tf.float32. + if set as None, it will be set to average color of the randomly + cropped image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + padded_image: padded image. + padded_boxes: boxes which is the same rank as input boxes. Boxes are in + normalized form. + cropped_labels: cropped labels. + if label_weights is not None also returns: + cropped_label_weights: cropped label weights. + if multiclass_scores is not None also returns: + cropped_multiclass_scores: cropped_multiclass_scores. + + """ + image_size = tf.shape(image) + image_height = image_size[0] + image_width = image_size[1] + result = random_crop_image( + image=image, + boxes=boxes, + labels=labels, + label_weights=label_weights, + label_confidences=label_confidences, + multiclass_scores=multiclass_scores, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + overlap_thresh=overlap_thresh, + clip_boxes=clip_boxes, + random_coef=random_coef, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + cropped_image, cropped_boxes, cropped_labels = result[:3] + + min_image_size = tf.cast( + tf.cast(tf.stack([image_height, image_width]), dtype=tf.float32) * + min_padded_size_ratio, + dtype=tf.int32) + max_image_size = tf.cast( + tf.cast(tf.stack([image_height, image_width]), dtype=tf.float32) * + max_padded_size_ratio, + dtype=tf.int32) + + padded_image, padded_boxes = random_pad_image( + cropped_image, + cropped_boxes, + min_image_size=min_image_size, + max_image_size=max_image_size, + pad_color=pad_color, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + cropped_padded_output = (padded_image, padded_boxes, cropped_labels) + + index = 3 + if label_weights is not None: + cropped_label_weights = result[index] + cropped_padded_output += (cropped_label_weights,) + index += 1 + + if label_confidences is not None: + cropped_label_confidences = result[index] + cropped_padded_output += (cropped_label_confidences,) + index += 1 + + if multiclass_scores is not None: + cropped_multiclass_scores = result[index] + cropped_padded_output += (cropped_multiclass_scores,) + + return cropped_padded_output + + +def random_crop_to_aspect_ratio(image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + aspect_ratio=1.0, + overlap_thresh=0.3, + clip_boxes=True, + seed=None, + preprocess_vars_cache=None): + """Randomly crops an image to the specified aspect ratio. + + Randomly crops the a portion of the image such that the crop is of the + specified aspect ratio, and the crop is as large as possible. If the specified + aspect ratio is larger than the aspect ratio of the image, this op will + randomly remove rows from the top and bottom of the image. If the specified + aspect ratio is less than the aspect ratio of the image, this op will randomly + remove cols from the left and right of the image. If the specified aspect + ratio is the same as the aspect ratio of the image, this op will return the + image. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: (optional) float32 tensor of shape [num_instances] + representing the confidence for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + aspect_ratio: the aspect ratio of cropped image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + + If label_weights, masks, keypoints, or multiclass_scores is not None, the + function also returns: + label_weights: rank 1 float32 tensor with shape [num_instances]. + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + multiclass_scores: rank 2 float32 tensor with shape + [num_instances, num_classes] + + Raises: + ValueError: If image is not a 3D tensor. + """ + if len(image.get_shape()) != 3: + raise ValueError('Image should be 3D tensor') + + with tf.name_scope('RandomCropToAspectRatio', values=[image]): + image_shape = tf.shape(image) + orig_height = image_shape[0] + orig_width = image_shape[1] + orig_aspect_ratio = tf.cast( + orig_width, dtype=tf.float32) / tf.cast( + orig_height, dtype=tf.float32) + new_aspect_ratio = tf.constant(aspect_ratio, dtype=tf.float32) + + def target_height_fn(): + return tf.cast( + tf.round(tf.cast(orig_width, dtype=tf.float32) / new_aspect_ratio), + dtype=tf.int32) + + target_height = tf.cond(orig_aspect_ratio >= new_aspect_ratio, + lambda: orig_height, target_height_fn) + + def target_width_fn(): + return tf.cast( + tf.round(tf.cast(orig_height, dtype=tf.float32) * new_aspect_ratio), + dtype=tf.int32) + + target_width = tf.cond(orig_aspect_ratio <= new_aspect_ratio, + lambda: orig_width, target_width_fn) + + # either offset_height = 0 and offset_width is randomly chosen from + # [0, offset_width - target_width), or else offset_width = 0 and + # offset_height is randomly chosen from [0, offset_height - target_height) + offset_height = _random_integer(0, orig_height - target_height + 1, seed) + offset_width = _random_integer(0, orig_width - target_width + 1, seed) + + generator_func = lambda: (offset_height, offset_width) + offset_height, offset_width = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.CROP_TO_ASPECT_RATIO, + preprocess_vars_cache) + + new_image = tf.image.crop_to_bounding_box( + image, offset_height, offset_width, target_height, target_width) + + im_box = tf.stack([ + tf.cast(offset_height, dtype=tf.float32) / + tf.cast(orig_height, dtype=tf.float32), + tf.cast(offset_width, dtype=tf.float32) / + tf.cast(orig_width, dtype=tf.float32), + tf.cast(offset_height + target_height, dtype=tf.float32) / + tf.cast(orig_height, dtype=tf.float32), + tf.cast(offset_width + target_width, dtype=tf.float32) / + tf.cast(orig_width, dtype=tf.float32) + ]) + + boxlist = box_list.BoxList(boxes) + boxlist.add_field('labels', labels) + + boxlist.add_field('label_weights', label_weights) + + if label_confidences is not None: + boxlist.add_field('label_confidences', label_confidences) + + if multiclass_scores is not None: + boxlist.add_field('multiclass_scores', multiclass_scores) + + im_boxlist = box_list.BoxList(tf.expand_dims(im_box, 0)) + + # remove boxes whose overlap with the image is less than overlap_thresh + overlapping_boxlist, keep_ids = box_list_ops.prune_non_overlapping_boxes( + boxlist, im_boxlist, overlap_thresh) + + # change the coordinate of the remaining boxes + new_labels = overlapping_boxlist.get_field('labels') + new_boxlist = box_list_ops.change_coordinate_frame(overlapping_boxlist, + im_box) + if clip_boxes: + new_boxlist = box_list_ops.clip_to_window( + new_boxlist, tf.constant([0.0, 0.0, 1.0, 1.0], tf.float32)) + new_boxes = new_boxlist.get() + + result = [new_image, new_boxes, new_labels] + + new_label_weights = overlapping_boxlist.get_field('label_weights') + result.append(new_label_weights) + + if label_confidences is not None: + new_label_confidences = ( + overlapping_boxlist.get_field('label_confidences')) + result.append(new_label_confidences) + + if multiclass_scores is not None: + new_multiclass_scores = overlapping_boxlist.get_field('multiclass_scores') + result.append(new_multiclass_scores) + + if masks is not None: + masks_inside_window = tf.gather(masks, keep_ids) + masks_box_begin = tf.stack([0, offset_height, offset_width]) + masks_box_size = tf.stack([-1, target_height, target_width]) + new_masks = tf.slice(masks_inside_window, masks_box_begin, masks_box_size) + result.append(new_masks) + + if keypoints is not None: + keypoints_inside_window = tf.gather(keypoints, keep_ids) + new_keypoints = keypoint_ops.change_coordinate_frame( + keypoints_inside_window, im_box) + if clip_boxes: + new_keypoints = keypoint_ops.prune_outside_window(new_keypoints, + [0.0, 0.0, 1.0, 1.0]) + result.append(new_keypoints) + + return tuple(result) + + +def random_pad_to_aspect_ratio(image, + boxes, + masks=None, + keypoints=None, + aspect_ratio=1.0, + min_padded_size_ratio=(1.0, 1.0), + max_padded_size_ratio=(2.0, 2.0), + seed=None, + preprocess_vars_cache=None): + """Randomly zero pads an image to the specified aspect ratio. + + Pads the image so that the resulting image will have the specified aspect + ratio without scaling less than the min_padded_size_ratio or more than the + max_padded_size_ratio. If the min_padded_size_ratio or max_padded_size_ratio + is lower than what is possible to maintain the aspect ratio, then this method + will use the least padding to achieve the specified aspect ratio. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + aspect_ratio: aspect ratio of the final image. + min_padded_size_ratio: min ratio of padded image height and width to the + input image's height and width. + max_padded_size_ratio: max ratio of padded image height and width to the + input image's height and width. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + + If masks, or keypoints is not None, the function also returns: + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + + Raises: + ValueError: If image is not a 3D tensor. + """ + if len(image.get_shape()) != 3: + raise ValueError('Image should be 3D tensor') + + with tf.name_scope('RandomPadToAspectRatio', values=[image]): + image_shape = tf.shape(image) + image_height = tf.cast(image_shape[0], dtype=tf.float32) + image_width = tf.cast(image_shape[1], dtype=tf.float32) + image_aspect_ratio = image_width / image_height + new_aspect_ratio = tf.constant(aspect_ratio, dtype=tf.float32) + target_height = tf.cond( + image_aspect_ratio <= new_aspect_ratio, + lambda: image_height, + lambda: image_width / new_aspect_ratio) + target_width = tf.cond( + image_aspect_ratio >= new_aspect_ratio, + lambda: image_width, + lambda: image_height * new_aspect_ratio) + + min_height = tf.maximum( + min_padded_size_ratio[0] * image_height, target_height) + min_width = tf.maximum( + min_padded_size_ratio[1] * image_width, target_width) + max_height = tf.maximum( + max_padded_size_ratio[0] * image_height, target_height) + max_width = tf.maximum( + max_padded_size_ratio[1] * image_width, target_width) + + max_scale = tf.minimum(max_height / target_height, max_width / target_width) + min_scale = tf.minimum( + max_scale, + tf.maximum(min_height / target_height, min_width / target_width)) + + generator_func = functools.partial(tf.random_uniform, [], + min_scale, max_scale, seed=seed) + scale = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.PAD_TO_ASPECT_RATIO, + preprocess_vars_cache) + + target_height = tf.round(scale * target_height) + target_width = tf.round(scale * target_width) + + new_image = tf.image.pad_to_bounding_box( + image, 0, 0, tf.cast(target_height, dtype=tf.int32), + tf.cast(target_width, dtype=tf.int32)) + + im_box = tf.stack([ + 0.0, + 0.0, + target_height / image_height, + target_width / image_width + ]) + boxlist = box_list.BoxList(boxes) + new_boxlist = box_list_ops.change_coordinate_frame(boxlist, im_box) + new_boxes = new_boxlist.get() + + result = [new_image, new_boxes] + + if masks is not None: + new_masks = tf.expand_dims(masks, -1) + new_masks = tf.image.pad_to_bounding_box( + new_masks, 0, 0, tf.cast(target_height, dtype=tf.int32), + tf.cast(target_width, dtype=tf.int32)) + new_masks = tf.squeeze(new_masks, [-1]) + result.append(new_masks) + + if keypoints is not None: + new_keypoints = keypoint_ops.change_coordinate_frame(keypoints, im_box) + result.append(new_keypoints) + + return tuple(result) + + +def random_black_patches(image, + max_black_patches=10, + probability=0.5, + size_to_image_ratio=0.1, + random_seed=None, + preprocess_vars_cache=None): + """Randomly adds some black patches to the image. + + This op adds up to max_black_patches square black patches of a fixed size + to the image where size is specified via the size_to_image_ratio parameter. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + max_black_patches: number of times that the function tries to add a + black box to the image. + probability: at each try, what is the chance of adding a box. + size_to_image_ratio: Determines the ratio of the size of the black patches + to the size of the image. + box_size = size_to_image_ratio * + min(image_width, image_height) + random_seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image + """ + def add_black_patch_to_image(image, idx): + """Function for adding one patch to the image. + + Args: + image: image + idx: counter for number of patches that could have been added + + Returns: + image with a randomly added black box + """ + image_shape = tf.shape(image) + image_height = image_shape[0] + image_width = image_shape[1] + box_size = tf.cast( + tf.multiply( + tf.minimum( + tf.cast(image_height, dtype=tf.float32), + tf.cast(image_width, dtype=tf.float32)), size_to_image_ratio), + dtype=tf.int32) + + generator_func = functools.partial(tf.random_uniform, [], minval=0.0, + maxval=(1.0 - size_to_image_ratio), + seed=random_seed) + normalized_y_min = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.ADD_BLACK_PATCH, + preprocess_vars_cache, key=str(idx) + 'y') + normalized_x_min = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.ADD_BLACK_PATCH, + preprocess_vars_cache, key=str(idx) + 'x') + + y_min = tf.cast( + normalized_y_min * tf.cast(image_height, dtype=tf.float32), + dtype=tf.int32) + x_min = tf.cast( + normalized_x_min * tf.cast(image_width, dtype=tf.float32), + dtype=tf.int32) + black_box = tf.ones([box_size, box_size, 3], dtype=tf.float32) + mask = 1.0 - tf.image.pad_to_bounding_box(black_box, y_min, x_min, + image_height, image_width) + image = tf.multiply(image, mask) + return image + + with tf.name_scope('RandomBlackPatchInImage', values=[image]): + for idx in range(max_black_patches): + generator_func = functools.partial(tf.random_uniform, [], + minval=0.0, maxval=1.0, + dtype=tf.float32, seed=random_seed) + random_prob = _get_or_create_preprocess_rand_vars( + generator_func, + preprocessor_cache.PreprocessorCache.BLACK_PATCHES, + preprocess_vars_cache, key=idx) + image = tf.cond( + tf.greater(random_prob, probability), lambda: image, + functools.partial(add_black_patch_to_image, image=image, idx=idx)) + return image + + +def random_jpeg_quality(image, + min_jpeg_quality=0, + max_jpeg_quality=100, + random_coef=0.0, + seed=None, + preprocess_vars_cache=None): + """Randomly encode the image to a random JPEG quality level. + + Args: + image: rank 3 float32 tensor with shape [height, width, channels] and + values in the range [0, 255]. + min_jpeg_quality: An int for the lower bound for selecting a random jpeg + quality level. + max_jpeg_quality: An int for the upper bound for selecting a random jpeg + quality level. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the encoded image, + and if it is 1.0, we will always get the original image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this function is called + multiple times with the same non-null cache, it will perform + deterministically. + + Returns: + image: image which is the same shape as input image. + """ + def _adjust_jpeg_quality(): + """Encodes the image as jpeg with a random quality and then decodes.""" + generator_func = functools.partial( + tf.random_uniform, [], + minval=min_jpeg_quality, + maxval=max_jpeg_quality, + dtype=tf.int32, + seed=seed) + quality = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.JPEG_QUALITY, + preprocess_vars_cache, key='quality') + + # Need to convert to uint8 before calling adjust_jpeg_quality since it + # assumes that float features are in the range [0, 1], where herein the + # range is [0, 255]. + image_uint8 = tf.cast(image, tf.uint8) + adjusted_image = tf.image.adjust_jpeg_quality(image_uint8, quality) + return tf.cast(adjusted_image, tf.float32) + + with tf.name_scope('RandomJpegQuality', values=[image]): + generator_func = functools.partial(tf.random_uniform, [], seed=seed) + do_encoding_random = _get_or_create_preprocess_rand_vars( + generator_func, preprocessor_cache.PreprocessorCache.JPEG_QUALITY, + preprocess_vars_cache) + do_encoding_random = tf.greater_equal(do_encoding_random, random_coef) + image = tf.cond(do_encoding_random, _adjust_jpeg_quality, + lambda: tf.cast(image, tf.float32)) + + return image + + +def random_downscale_to_target_pixels(image, + masks=None, + min_target_pixels=300000, + max_target_pixels=800000, + random_coef=0.0, + seed=None, + preprocess_vars_cache=None): + """Randomly downscales the image to a target number of pixels. + + If the image contains less than the chosen target number of pixels, it will + not be downscaled. + + Args: + image: Rank 3 float32 tensor with shape [height, width, channels] and + values in the range [0, 255]. + masks: (optional) Rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks are of + the same height, width as the input `image`. + min_target_pixels: Integer. An inclusive lower bound for for the target + number of pixels. + max_target_pixels: Integer. An exclusive upper bound for for the target + number of pixels. + random_coef: Float. Random coefficient that defines the chance of getting + the original image. If random_coef is 0, we will always apply downscaling, + and if it is 1.0, we will always get the original image. + seed: (optional) Integer. Random seed. + preprocess_vars_cache: (optional) PreprocessorCache object that records + previously performed augmentations. Updated in-place. If this function is + called multiple times with the same non-null cache, it will perform + deterministically. + + Returns: + Tuple with elements: + image: Resized image which is the same rank as input image. + masks: If masks is not None, resized masks which are the same rank as + the input masks. + + Raises: + ValueError: If min_target_pixels or max_target_pixels are not positive. + """ + if min_target_pixels <= 0: + raise ValueError('Minimum target pixels must be positive') + if max_target_pixels <= 0: + raise ValueError('Maximum target pixels must be positive') + + def _resize_image_to_target(target_height, target_width): + # pylint: disable=unbalanced-tuple-unpacking + new_image, _ = resize_image(image, None, target_height, target_width) + return (new_image,) + + def _resize_image_and_masks_to_target(target_height, target_width): + # pylint: disable=unbalanced-tuple-unpacking + new_image, new_masks, _ = resize_image(image, masks, target_height, + target_width) + return new_image, new_masks + + with tf.name_scope('RandomDownscaleToTargetPixels', values=[image]): + generator_fn = functools.partial(tf.random_uniform, [], seed=seed) + do_downscale_random = _get_or_create_preprocess_rand_vars( + generator_fn, + preprocessor_cache.PreprocessorCache.DOWNSCALE_TO_TARGET_PIXELS, + preprocess_vars_cache) + do_downscale_random = tf.greater_equal(do_downscale_random, random_coef) + + generator_fn = functools.partial( + tf.random_uniform, [], + minval=min_target_pixels, + maxval=max_target_pixels, + dtype=tf.int32, + seed=seed) + target_pixels = _get_or_create_preprocess_rand_vars( + generator_fn, + preprocessor_cache.PreprocessorCache.DOWNSCALE_TO_TARGET_PIXELS, + preprocess_vars_cache, + key='target_pixels') + + image_shape = tf.shape(image) + image_height = image_shape[0] + image_width = image_shape[1] + image_pixels = image_height * image_width + scale_factor = tf.sqrt( + tf.cast(target_pixels, dtype=tf.float32) / + tf.cast(image_pixels, dtype=tf.float32)) + target_height = tf.cast( + scale_factor * tf.cast(image_height, dtype=tf.float32), dtype=tf.int32) + target_width = tf.cast( + scale_factor * tf.cast(image_width, dtype=tf.float32), dtype=tf.int32) + image_larger_than_target = tf.greater(image_pixels, target_pixels) + + should_apply_resize = tf.logical_and(do_downscale_random, + image_larger_than_target) + if masks is not None: + resize_fn = functools.partial(_resize_image_and_masks_to_target, + target_height, target_width) + return tf.cond(should_apply_resize, resize_fn, + lambda: (tf.cast(image, dtype=tf.float32), masks)) + else: + resize_fn = lambda: _resize_image_to_target(target_height, target_width) + return tf.cond(should_apply_resize, resize_fn, + lambda: (tf.cast(image, dtype=tf.float32),)) + + +def random_patch_gaussian(image, + min_patch_size=1, + max_patch_size=250, + min_gaussian_stddev=0.0, + max_gaussian_stddev=1.0, + random_coef=0.0, + seed=None, + preprocess_vars_cache=None): + """Randomly applies gaussian noise to a random patch on the image. + + The gaussian noise is applied to the image with values scaled to the range + [0.0, 1.0]. The result of applying gaussian noise to the scaled image is + clipped to be within the range [0.0, 1.0], equivalent to the range + [0.0, 255.0] after rescaling the image back. + + See "Improving Robustness Without Sacrificing Accuracy with Patch Gaussian + Augmentation " by Lopes et al., 2019, for further details. + https://arxiv.org/abs/1906.02611 + + Args: + image: Rank 3 float32 tensor with shape [height, width, channels] and + values in the range [0.0, 255.0]. + min_patch_size: Integer. An inclusive lower bound for the patch size. + max_patch_size: Integer. An exclusive upper bound for the patch size. + min_gaussian_stddev: Float. An inclusive lower bound for the standard + deviation of the gaussian noise. + max_gaussian_stddev: Float. An exclusive upper bound for the standard + deviation of the gaussian noise. + random_coef: Float. Random coefficient that defines the chance of getting + the original image. If random_coef is 0.0, we will always apply + downscaling, and if it is 1.0, we will always get the original image. + seed: (optional) Integer. Random seed. + preprocess_vars_cache: (optional) PreprocessorCache object that records + previously performed augmentations. Updated in-place. If this function is + called multiple times with the same non-null cache, it will perform + deterministically. + + Returns: + Rank 3 float32 tensor with same shape as the input image and with gaussian + noise applied within a random patch. + + Raises: + ValueError: If min_patch_size is < 1. + """ + if min_patch_size < 1: + raise ValueError('Minimum patch size must be >= 1.') + + get_or_create_rand_vars_fn = functools.partial( + _get_or_create_preprocess_rand_vars, + function_id=preprocessor_cache.PreprocessorCache.PATCH_GAUSSIAN, + preprocess_vars_cache=preprocess_vars_cache) + + def _apply_patch_gaussian(image): + """Applies a patch gaussian with random size, location, and stddev.""" + patch_size = get_or_create_rand_vars_fn( + functools.partial( + tf.random_uniform, [], + minval=min_patch_size, + maxval=max_patch_size, + dtype=tf.int32, + seed=seed), + key='patch_size') + gaussian_stddev = get_or_create_rand_vars_fn( + functools.partial( + tf.random_uniform, [], + minval=min_gaussian_stddev, + maxval=max_gaussian_stddev, + dtype=tf.float32, + seed=seed), + key='gaussian_stddev') + + image_shape = tf.shape(image) + y = get_or_create_rand_vars_fn( + functools.partial( + tf.random_uniform, [], + minval=0, + maxval=image_shape[0], + dtype=tf.int32, + seed=seed), + key='y') + x = get_or_create_rand_vars_fn( + functools.partial( + tf.random_uniform, [], + minval=0, + maxval=image_shape[1], + dtype=tf.int32, + seed=seed), + key='x') + gaussian = get_or_create_rand_vars_fn( + functools.partial( + tf.random.normal, + image_shape, + stddev=gaussian_stddev, + dtype=tf.float32, + seed=seed), + key='gaussian') + + scaled_image = image / 255.0 + image_plus_gaussian = tf.clip_by_value(scaled_image + gaussian, 0.0, 1.0) + patch_mask = patch_ops.get_patch_mask(y, x, patch_size, image_shape) + patch_mask = tf.expand_dims(patch_mask, -1) + patch_mask = tf.tile(patch_mask, [1, 1, image_shape[2]]) + patched_image = tf.where(patch_mask, image_plus_gaussian, scaled_image) + return patched_image * 255.0 + + with tf.name_scope('RandomPatchGaussian', values=[image]): + image = tf.cast(image, tf.float32) + patch_gaussian_random = get_or_create_rand_vars_fn( + functools.partial(tf.random_uniform, [], seed=seed)) + do_patch_gaussian = tf.greater_equal(patch_gaussian_random, random_coef) + image = tf.cond(do_patch_gaussian, + lambda: _apply_patch_gaussian(image), + lambda: image) + return image + + +# TODO(barretzoph): Put in AutoAugment Paper link when paper is live. +def autoaugment_image(image, boxes, policy_name='v0'): + """Apply an autoaugment policy to the image and boxes. + + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 255]. + boxes: rank 2 float32 tensor containing the bounding boxes with shape + [num_instances, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + policy_name: The name of the AutoAugment policy to use. The available + options are `v0`, `v1`, `v2`, `v3` and `test`. `v0` is the policy used for + all of the results in the paper and was found to achieve the best results + on the COCO dataset. `v1`, `v2` and `v3` are additional good policies + found on the COCO dataset that have slight variation in what operations + were used during the search procedure along with how many operations are + applied in parallel to a single image (2 vs 3). + + + Returns: + image: the augmented image. + boxes: boxes which is the same rank as input boxes. Boxes are in normalized + form. boxes will have been augmented along with image. + """ + return autoaugment_utils.distort_image_with_autoaugment( + image, boxes, policy_name) + + +def image_to_float(image): + """Used in Faster R-CNN. Casts image pixel values to float. + + Args: + image: input image which might be in tf.uint8 or sth else format + + Returns: + image: image in tf.float32 format. + """ + with tf.name_scope('ImageToFloat', values=[image]): + image = tf.cast(image, dtype=tf.float32) + return image + + +def random_resize_method(image, target_size, preprocess_vars_cache=None): + """Uses a random resize method to resize the image to target size. + + Args: + image: a rank 3 tensor. + target_size: a list of [target_height, target_width] + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + resized image. + """ + + resized_image = _apply_with_random_selector( + image, + lambda x, method: tf.image.resize_images(x, target_size, method), + num_cases=4, + preprocess_vars_cache=preprocess_vars_cache, + key=preprocessor_cache.PreprocessorCache.RESIZE_METHOD) + + return resized_image + + +def resize_to_range(image, + masks=None, + min_dimension=None, + max_dimension=None, + method=tf.image.ResizeMethod.BILINEAR, + align_corners=False, + pad_to_max_dimension=False, + per_channel_pad_value=(0, 0, 0)): + """Resizes an image so its dimensions are within the provided value. + + The output size can be described by two cases: + 1. If the image can be rescaled so its minimum dimension is equal to the + provided value without the other dimension exceeding max_dimension, + then do so. + 2. Otherwise, resize so the largest dimension is equal to max_dimension. + + Args: + image: A 3D tensor of shape [height, width, channels] + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. + min_dimension: (optional) (scalar) desired size of the smaller image + dimension. + max_dimension: (optional) (scalar) maximum allowed size + of the larger image dimension. + method: (optional) interpolation method used in resizing. Defaults to + BILINEAR. + align_corners: bool. If true, exactly align all 4 corners of the input + and output. Defaults to False. + pad_to_max_dimension: Whether to resize the image and pad it with zeros + so the resulting image is of the spatial size + [max_dimension, max_dimension]. If masks are included they are padded + similarly. + per_channel_pad_value: A tuple of per-channel scalar value to use for + padding. By default pads zeros. + + Returns: + Note that the position of the resized_image_shape changes based on whether + masks are present. + resized_image: A 3D tensor of shape [new_height, new_width, channels], + where the image has been resized (with bilinear interpolation) so that + min(new_height, new_width) == min_dimension or + max(new_height, new_width) == max_dimension. + resized_masks: If masks is not None, also outputs masks. A 3D tensor of + shape [num_instances, new_height, new_width]. + resized_image_shape: A 1D tensor of shape [3] containing shape of the + resized image. + + Raises: + ValueError: if the image is not a 3D tensor. + """ + if len(image.get_shape()) != 3: + raise ValueError('Image should be 3D tensor') + + def _resize_landscape_image(image): + # resize a landscape image + return tf.image.resize_images( + image, tf.stack([min_dimension, max_dimension]), method=method, + align_corners=align_corners, preserve_aspect_ratio=True) + + def _resize_portrait_image(image): + # resize a portrait image + return tf.image.resize_images( + image, tf.stack([max_dimension, min_dimension]), method=method, + align_corners=align_corners, preserve_aspect_ratio=True) + + with tf.name_scope('ResizeToRange', values=[image, min_dimension]): + if image.get_shape().is_fully_defined(): + if image.get_shape()[0] < image.get_shape()[1]: + new_image = _resize_landscape_image(image) + else: + new_image = _resize_portrait_image(image) + new_size = tf.constant(new_image.get_shape().as_list()) + else: + new_image = tf.cond( + tf.less(tf.shape(image)[0], tf.shape(image)[1]), + lambda: _resize_landscape_image(image), + lambda: _resize_portrait_image(image)) + new_size = tf.shape(new_image) + + if pad_to_max_dimension: + channels = tf.unstack(new_image, axis=2) + if len(channels) != len(per_channel_pad_value): + raise ValueError('Number of channels must be equal to the length of ' + 'per-channel pad value.') + new_image = tf.stack( + [ + tf.pad( + channels[i], [[0, max_dimension - new_size[0]], + [0, max_dimension - new_size[1]]], + constant_values=per_channel_pad_value[i]) + for i in range(len(channels)) + ], + axis=2) + new_image.set_shape([max_dimension, max_dimension, 3]) + + result = [new_image] + if masks is not None: + new_masks = tf.expand_dims(masks, 3) + new_masks = tf.image.resize_images( + new_masks, + new_size[:-1], + method=tf.image.ResizeMethod.NEAREST_NEIGHBOR, + align_corners=align_corners) + if pad_to_max_dimension: + new_masks = tf.image.pad_to_bounding_box( + new_masks, 0, 0, max_dimension, max_dimension) + new_masks = tf.squeeze(new_masks, 3) + result.append(new_masks) + + result.append(new_size) + return result + + +def _get_image_info(image): + """Returns the height, width and number of channels in the image.""" + image_height = tf.shape(image)[0] + image_width = tf.shape(image)[1] + num_channels = tf.shape(image)[2] + return (image_height, image_width, num_channels) + + +# TODO(alirezafathi): Make sure the static shapes are preserved. +def resize_to_min_dimension(image, masks=None, min_dimension=600, + method=tf.image.ResizeMethod.BILINEAR): + """Resizes image and masks given the min size maintaining the aspect ratio. + + If one of the image dimensions is smaller than min_dimension, it will scale + the image such that its smallest dimension is equal to min_dimension. + Otherwise, will keep the image size as is. + + Args: + image: a tensor of size [height, width, channels]. + masks: (optional) a tensors of size [num_instances, height, width]. + min_dimension: minimum image dimension. + method: (optional) interpolation method used in resizing. Defaults to + BILINEAR. + + Returns: + An array containing resized_image, resized_masks, and resized_image_shape. + Note that the position of the resized_image_shape changes based on whether + masks are present. + resized_image: A tensor of size [new_height, new_width, channels]. + resized_masks: If masks is not None, also outputs masks. A 3D tensor of + shape [num_instances, new_height, new_width] + resized_image_shape: A 1D tensor of shape [3] containing the shape of the + resized image. + + Raises: + ValueError: if the image is not a 3D tensor. + """ + if len(image.get_shape()) != 3: + raise ValueError('Image should be 3D tensor') + + with tf.name_scope('ResizeGivenMinDimension', values=[image, min_dimension]): + (image_height, image_width, num_channels) = _get_image_info(image) + min_image_dimension = tf.minimum(image_height, image_width) + min_target_dimension = tf.maximum(min_image_dimension, min_dimension) + target_ratio = tf.cast(min_target_dimension, dtype=tf.float32) / tf.cast( + min_image_dimension, dtype=tf.float32) + target_height = tf.cast( + tf.cast(image_height, dtype=tf.float32) * target_ratio, dtype=tf.int32) + target_width = tf.cast( + tf.cast(image_width, dtype=tf.float32) * target_ratio, dtype=tf.int32) + image = tf.image.resize_images( + tf.expand_dims(image, axis=0), size=[target_height, target_width], + method=method, + align_corners=True) + result = [tf.squeeze(image, axis=0)] + + if masks is not None: + masks = tf.image.resize_nearest_neighbor( + tf.expand_dims(masks, axis=3), + size=[target_height, target_width], + align_corners=True) + result.append(tf.squeeze(masks, axis=3)) + + result.append(tf.stack([target_height, target_width, num_channels])) + return result + + +def resize_to_max_dimension(image, masks=None, max_dimension=600, + method=tf.image.ResizeMethod.BILINEAR): + """Resizes image and masks given the max size maintaining the aspect ratio. + + If one of the image dimensions is greater than max_dimension, it will scale + the image such that its largest dimension is equal to max_dimension. + Otherwise, will keep the image size as is. + + Args: + image: a tensor of size [height, width, channels]. + masks: (optional) a tensors of size [num_instances, height, width]. + max_dimension: maximum image dimension. + method: (optional) interpolation method used in resizing. Defaults to + BILINEAR. + + Returns: + An array containing resized_image, resized_masks, and resized_image_shape. + Note that the position of the resized_image_shape changes based on whether + masks are present. + resized_image: A tensor of size [new_height, new_width, channels]. + resized_masks: If masks is not None, also outputs masks. A 3D tensor of + shape [num_instances, new_height, new_width] + resized_image_shape: A 1D tensor of shape [3] containing the shape of the + resized image. + + Raises: + ValueError: if the image is not a 3D tensor. + """ + if len(image.get_shape()) != 3: + raise ValueError('Image should be 3D tensor') + + with tf.name_scope('ResizeGivenMaxDimension', values=[image, max_dimension]): + (image_height, image_width, num_channels) = _get_image_info(image) + max_image_dimension = tf.maximum(image_height, image_width) + max_target_dimension = tf.minimum(max_image_dimension, max_dimension) + target_ratio = tf.cast(max_target_dimension, dtype=tf.float32) / tf.cast( + max_image_dimension, dtype=tf.float32) + target_height = tf.cast( + tf.cast(image_height, dtype=tf.float32) * target_ratio, dtype=tf.int32) + target_width = tf.cast( + tf.cast(image_width, dtype=tf.float32) * target_ratio, dtype=tf.int32) + image = tf.image.resize_images( + tf.expand_dims(image, axis=0), size=[target_height, target_width], + method=method, + align_corners=True) + result = [tf.squeeze(image, axis=0)] + + if masks is not None: + masks = tf.image.resize_nearest_neighbor( + tf.expand_dims(masks, axis=3), + size=[target_height, target_width], + align_corners=True) + result.append(tf.squeeze(masks, axis=3)) + + result.append(tf.stack([target_height, target_width, num_channels])) + return result + + +def resize_pad_to_multiple(image, masks=None, multiple=1): + """Resize an image by zero padding it to the specified multiple. + + For example, with an image of size (101, 199, 3) and multiple=4, + the returned image will have shape (104, 200, 3). + + Args: + image: a tensor of shape [height, width, channels] + masks: (optional) a tensor of shape [num_instances, height, width] + multiple: int, the multiple to which the height and width of the input + will be padded. + + Returns: + resized_image: The image with 0 padding applied, such that output + dimensions are divisible by `multiple` + resized_masks: If masks are given, they are resized to the same + spatial dimensions as the image. + resized_image_shape: An integer tensor of shape [3] which holds + the shape of the input image. + + """ + + if len(image.get_shape()) != 3: + raise ValueError('Image should be 3D tensor') + + with tf.name_scope('ResizePadToMultiple', values=[image, multiple]): + image_height, image_width, num_channels = _get_image_info(image) + image = image[tf.newaxis, :, :, :] + image = ops.pad_to_multiple(image, multiple)[0, :, :, :] + result = [image] + + if masks is not None: + masks = tf.transpose(masks, (1, 2, 0)) + masks = masks[tf.newaxis, :, :, :] + + masks = ops.pad_to_multiple(masks, multiple)[0, :, :, :] + masks = tf.transpose(masks, (2, 0, 1)) + result.append(masks) + + result.append(tf.stack([image_height, image_width, num_channels])) + return result + + +def scale_boxes_to_pixel_coordinates(image, boxes, keypoints=None): + """Scales boxes from normalized to pixel coordinates. + + Args: + image: A 3D float32 tensor of shape [height, width, channels]. + boxes: A 2D float32 tensor of shape [num_boxes, 4] containing the bounding + boxes in normalized coordinates. Each row is of the form + [ymin, xmin, ymax, xmax]. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x normalized + coordinates. + + Returns: + image: unchanged input image. + scaled_boxes: a 2D float32 tensor of shape [num_boxes, 4] containing the + bounding boxes in pixel coordinates. + scaled_keypoints: a 3D float32 tensor with shape + [num_instances, num_keypoints, 2] containing the keypoints in pixel + coordinates. + """ + boxlist = box_list.BoxList(boxes) + image_height = tf.shape(image)[0] + image_width = tf.shape(image)[1] + scaled_boxes = box_list_ops.scale(boxlist, image_height, image_width).get() + result = [image, scaled_boxes] + if keypoints is not None: + scaled_keypoints = keypoint_ops.scale(keypoints, image_height, image_width) + result.append(scaled_keypoints) + return tuple(result) + + +# TODO(alirezafathi): Investigate if instead the function should return None if +# masks is None. +# pylint: disable=g-doc-return-or-yield +def resize_image(image, + masks=None, + new_height=600, + new_width=1024, + method=tf.image.ResizeMethod.BILINEAR, + align_corners=False): + """Resizes images to the given height and width. + + Args: + image: A 3D tensor of shape [height, width, channels] + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. + new_height: (optional) (scalar) desired height of the image. + new_width: (optional) (scalar) desired width of the image. + method: (optional) interpolation method used in resizing. Defaults to + BILINEAR. + align_corners: bool. If true, exactly align all 4 corners of the input + and output. Defaults to False. + + Returns: + Note that the position of the resized_image_shape changes based on whether + masks are present. + resized_image: A tensor of size [new_height, new_width, channels]. + resized_masks: If masks is not None, also outputs masks. A 3D tensor of + shape [num_instances, new_height, new_width] + resized_image_shape: A 1D tensor of shape [3] containing the shape of the + resized image. + """ + with tf.name_scope( + 'ResizeImage', + values=[image, new_height, new_width, method, align_corners]): + new_image = tf.image.resize_images( + image, tf.stack([new_height, new_width]), + method=method, + align_corners=align_corners) + image_shape = shape_utils.combined_static_and_dynamic_shape(image) + result = [new_image] + if masks is not None: + num_instances = tf.shape(masks)[0] + new_size = tf.stack([new_height, new_width]) + def resize_masks_branch(): + new_masks = tf.expand_dims(masks, 3) + new_masks = tf.image.resize_nearest_neighbor( + new_masks, new_size, align_corners=align_corners) + new_masks = tf.squeeze(new_masks, axis=3) + return new_masks + + def reshape_masks_branch(): + # The shape function will be computed for both branches of the + # condition, regardless of which branch is actually taken. Make sure + # that we don't trigger an assertion in the shape function when trying + # to reshape a non empty tensor into an empty one. + new_masks = tf.reshape(masks, [-1, new_size[0], new_size[1]]) + return new_masks + + masks = tf.cond(num_instances > 0, resize_masks_branch, + reshape_masks_branch) + result.append(masks) + + result.append(tf.stack([new_height, new_width, image_shape[2]])) + return result + + +def subtract_channel_mean(image, means=None): + """Normalizes an image by subtracting a mean from each channel. + + Args: + image: A 3D tensor of shape [height, width, channels] + means: float list containing a mean for each channel + Returns: + normalized_images: a tensor of shape [height, width, channels] + Raises: + ValueError: if images is not a 4D tensor or if the number of means is not + equal to the number of channels. + """ + with tf.name_scope('SubtractChannelMean', values=[image, means]): + if len(image.get_shape()) != 3: + raise ValueError('Input must be of size [height, width, channels]') + if len(means) != image.get_shape()[-1]: + raise ValueError('len(means) must match the number of channels') + return image - [[means]] + + +def one_hot_encoding(labels, num_classes=None): + """One-hot encodes the multiclass labels. + + Example usage: + labels = tf.constant([1, 4], dtype=tf.int32) + one_hot = OneHotEncoding(labels, num_classes=5) + one_hot.eval() # evaluates to [0, 1, 0, 0, 1] + + Args: + labels: A tensor of shape [None] corresponding to the labels. + num_classes: Number of classes in the dataset. + Returns: + onehot_labels: a tensor of shape [num_classes] corresponding to the one hot + encoding of the labels. + Raises: + ValueError: if num_classes is not specified. + """ + with tf.name_scope('OneHotEncoding', values=[labels]): + if num_classes is None: + raise ValueError('num_classes must be specified') + + labels = tf.one_hot(labels, num_classes, 1, 0) + return tf.reduce_max(labels, 0) + + +def rgb_to_gray(image): + """Converts a 3 channel RGB image to a 1 channel grayscale image. + + Args: + image: Rank 3 float32 tensor containing 1 image -> [height, width, 3] + with pixel values varying between [0, 1]. + + Returns: + image: A single channel grayscale image -> [image, height, 1]. + """ + return _rgb_to_grayscale(image) + + +def random_self_concat_image( + image, boxes, labels, label_weights, label_confidences=None, + multiclass_scores=None, concat_vertical_probability=0.1, + concat_horizontal_probability=0.1, seed=None, + preprocess_vars_cache=None): + """Randomly concatenates the image with itself. + + This function randomly concatenates the image with itself; the random + variables for vertical and horizontal concatenation are independent. + Afterwards, we adjust the old bounding boxes, and add new bounding boxes + for the new objects. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: rank 1 float32 containing the label weights. + label_confidences: (optional) rank 1 float32 containing the label + confidences. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for + each box for each class. + concat_vertical_probability: (optional) a tf.float32 scalar denoting the + probability of a vertical concatenation. + concat_horizontal_probability: (optional) a tf.float32 scalar denoting the + probability of a horizontal concatenation. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: Image shape will be [new_height, new_width, channels]. + boxes: boxes which is the same rank as input boxes. Boxes are in normalized + form. + if label_confidences is not None also returns: + maybe_concat_label_confidences: cropped label weights. + if multiclass_scores is not None also returns: + maybe_concat_multiclass_scores: cropped_multiclass_scores. + """ + + concat_vertical = (tf.random_uniform([], seed=seed) < + concat_vertical_probability) + # Note the seed + 1 so we get some semblance of independence even with + # fixed seeds. + concat_horizontal = (tf.random_uniform([], seed=seed + 1 if seed else None) + < concat_horizontal_probability) + + gen_func = lambda: (concat_vertical, concat_horizontal) + params = _get_or_create_preprocess_rand_vars( + gen_func, preprocessor_cache.PreprocessorCache.SELF_CONCAT_IMAGE, + preprocess_vars_cache) + concat_vertical, concat_horizontal = params + + def _concat_image(image, boxes, labels, label_weights, axis): + """Concats the image to itself on `axis`.""" + output_images = tf.concat([image, image], axis=axis) + + if axis == 0: + # Concat vertically, so need to reduce the y coordinates. + old_scaling = tf.constant([0.5, 1.0, 0.5, 1.0]) + new_translation = tf.constant([0.5, 0.0, 0.5, 0.0]) + elif axis == 1: + old_scaling = tf.constant([1.0, 0.5, 1.0, 0.5]) + new_translation = tf.constant([0.0, 0.5, 0.0, 0.5]) + + old_boxes = old_scaling * boxes + new_boxes = old_boxes + new_translation + all_boxes = tf.concat([old_boxes, new_boxes], axis=0) + + return [output_images, all_boxes, tf.tile(labels, [2]), tf.tile( + label_weights, [2])] + + image, boxes, labels, label_weights = tf.cond( + concat_vertical, + lambda: _concat_image(image, boxes, labels, label_weights, axis=0), + lambda: [image, boxes, labels, label_weights], + strict=True) + + outputs = tf.cond( + concat_horizontal, + lambda: _concat_image(image, boxes, labels, label_weights, axis=1), + lambda: [image, boxes, labels, label_weights], + strict=True) + + if label_confidences is not None: + label_confidences = tf.cond(concat_vertical, + lambda: tf.tile(label_confidences, [2]), + lambda: label_confidences) + outputs.append(tf.cond(concat_horizontal, + lambda: tf.tile(label_confidences, [2]), + lambda: label_confidences)) + + if multiclass_scores is not None: + multiclass_scores = tf.cond(concat_vertical, + lambda: tf.tile(multiclass_scores, [2, 1]), + lambda: multiclass_scores) + outputs.append(tf.cond(concat_horizontal, + lambda: tf.tile(multiclass_scores, [2, 1]), + lambda: multiclass_scores)) + + return outputs + + +def ssd_random_crop(image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + min_object_covered=(0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + aspect_ratio_range=((0.5, 2.0),) * 7, + area_range=((0.1, 1.0),) * 7, + overlap_thresh=(0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + clip_boxes=(True,) * 7, + random_coef=(0.15,) * 7, + seed=None, + preprocess_vars_cache=None): + """Random crop preprocessing with default parameters as in SSD paper. + + Liu et al., SSD: Single shot multibox detector. + For further information on random crop preprocessing refer to RandomCrop + function above. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: rank 1 float32 tensor containing the weights. + label_confidences: rank 1 float32 tensor containing the confidences. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio_range: allowed range for aspect ratio of cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the + cropped image, and if it is 1.0, we will always get the + original image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + + If label_weights, multiclass_scores, masks, or keypoints is not None, the + function also returns: + label_weights: rank 1 float32 tensor with shape [num_instances]. + multiclass_scores: rank 2 float32 tensor with shape + [num_instances, num_classes] + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + """ + + def random_crop_selector(selected_result, index): + """Applies random_crop_image to selected result. + + Args: + selected_result: A tuple containing image, boxes, labels, keypoints (if + not None), and masks (if not None). + index: The index that was randomly selected. + + Returns: A tuple containing image, boxes, labels, keypoints (if not None), + and masks (if not None). + """ + + i = 3 + image, boxes, labels = selected_result[:i] + selected_label_weights = None + selected_label_confidences = None + selected_multiclass_scores = None + selected_masks = None + selected_keypoints = None + if label_weights is not None: + selected_label_weights = selected_result[i] + i += 1 + if label_confidences is not None: + selected_label_confidences = selected_result[i] + i += 1 + if multiclass_scores is not None: + selected_multiclass_scores = selected_result[i] + i += 1 + if masks is not None: + selected_masks = selected_result[i] + i += 1 + if keypoints is not None: + selected_keypoints = selected_result[i] + + return random_crop_image( + image=image, + boxes=boxes, + labels=labels, + label_weights=selected_label_weights, + label_confidences=selected_label_confidences, + multiclass_scores=selected_multiclass_scores, + masks=selected_masks, + keypoints=selected_keypoints, + min_object_covered=min_object_covered[index], + aspect_ratio_range=aspect_ratio_range[index], + area_range=area_range[index], + overlap_thresh=overlap_thresh[index], + clip_boxes=clip_boxes[index], + random_coef=random_coef[index], + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + result = _apply_with_random_selector_tuples( + tuple( + t for t in (image, boxes, labels, label_weights, label_confidences, + multiclass_scores, masks, keypoints) if t is not None), + random_crop_selector, + num_cases=len(min_object_covered), + preprocess_vars_cache=preprocess_vars_cache, + key=preprocessor_cache.PreprocessorCache.SSD_CROP_SELECTOR_ID) + return result + + +def ssd_random_crop_pad(image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + min_object_covered=(0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + aspect_ratio_range=((0.5, 2.0),) * 6, + area_range=((0.1, 1.0),) * 6, + overlap_thresh=(0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + clip_boxes=(True,) * 6, + random_coef=(0.15,) * 6, + min_padded_size_ratio=((1.0, 1.0),) * 6, + max_padded_size_ratio=((2.0, 2.0),) * 6, + pad_color=(None,) * 6, + seed=None, + preprocess_vars_cache=None): + """Random crop preprocessing with default parameters as in SSD paper. + + Liu et al., SSD: Single shot multibox detector. + For further information on random crop preprocessing refer to RandomCrop + function above. + + Args: + image: rank 3 float32 tensor containing 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: float32 tensor of shape [num_instances] representing the + confidences for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio_range: allowed range for aspect ratio of cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the + cropped image, and if it is 1.0, we will always get the + original image. + min_padded_size_ratio: min ratio of padded image height and width to the + input image's height and width. + max_padded_size_ratio: max ratio of padded image height and width to the + input image's height and width. + pad_color: padding color. A rank 1 tensor of [3] with dtype=tf.float32. + if set as None, it will be set to average color of the randomly + cropped image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: Image shape will be [new_height, new_width, channels]. + boxes: boxes which is the same rank as input boxes. Boxes are in normalized + form. + new_labels: new labels. + new_label_weights: new label weights. + """ + + def random_crop_pad_selector(image_boxes_labels, index): + """Random crop preprocessing helper.""" + i = 3 + image, boxes, labels = image_boxes_labels[:i] + selected_label_weights = None + selected_label_confidences = None + selected_multiclass_scores = None + if label_weights is not None: + selected_label_weights = image_boxes_labels[i] + i += 1 + if label_confidences is not None: + selected_label_confidences = image_boxes_labels[i] + i += 1 + if multiclass_scores is not None: + selected_multiclass_scores = image_boxes_labels[i] + + return random_crop_pad_image( + image, + boxes, + labels, + label_weights=selected_label_weights, + label_confidences=selected_label_confidences, + multiclass_scores=selected_multiclass_scores, + min_object_covered=min_object_covered[index], + aspect_ratio_range=aspect_ratio_range[index], + area_range=area_range[index], + overlap_thresh=overlap_thresh[index], + clip_boxes=clip_boxes[index], + random_coef=random_coef[index], + min_padded_size_ratio=min_padded_size_ratio[index], + max_padded_size_ratio=max_padded_size_ratio[index], + pad_color=pad_color[index], + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + return _apply_with_random_selector_tuples( + tuple(t for t in (image, boxes, labels, label_weights, label_confidences, + multiclass_scores) if t is not None), + random_crop_pad_selector, + num_cases=len(min_object_covered), + preprocess_vars_cache=preprocess_vars_cache, + key=preprocessor_cache.PreprocessorCache.SSD_CROP_PAD_SELECTOR_ID) + + +def ssd_random_crop_fixed_aspect_ratio( + image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + min_object_covered=(0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + aspect_ratio=1.0, + area_range=((0.1, 1.0),) * 7, + overlap_thresh=(0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + clip_boxes=(True,) * 7, + random_coef=(0.15,) * 7, + seed=None, + preprocess_vars_cache=None): + """Random crop preprocessing with default parameters as in SSD paper. + + Liu et al., SSD: Single shot multibox detector. + For further information on random crop preprocessing refer to RandomCrop + function above. + + The only difference is that the aspect ratio of the crops are fixed. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: (optional) float32 tensor of shape [num_instances] + representing the confidences for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio: aspect ratio of the cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the + cropped image, and if it is 1.0, we will always get the + original image. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + + If multiclass_scores, masks, or keypoints is not None, the function also + returns: + + multiclass_scores: rank 2 float32 tensor with shape + [num_instances, num_classes] + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + """ + aspect_ratio_range = ((aspect_ratio, aspect_ratio),) * len(area_range) + + crop_result = ssd_random_crop( + image, + boxes, + labels, + label_weights=label_weights, + label_confidences=label_confidences, + multiclass_scores=multiclass_scores, + masks=masks, + keypoints=keypoints, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + overlap_thresh=overlap_thresh, + clip_boxes=clip_boxes, + random_coef=random_coef, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + i = 3 + new_image, new_boxes, new_labels = crop_result[:i] + new_label_weights = None + new_label_confidences = None + new_multiclass_scores = None + new_masks = None + new_keypoints = None + if label_weights is not None: + new_label_weights = crop_result[i] + i += 1 + if label_confidences is not None: + new_label_confidences = crop_result[i] + i += 1 + if multiclass_scores is not None: + new_multiclass_scores = crop_result[i] + i += 1 + if masks is not None: + new_masks = crop_result[i] + i += 1 + if keypoints is not None: + new_keypoints = crop_result[i] + + result = random_crop_to_aspect_ratio( + new_image, + new_boxes, + new_labels, + label_weights=new_label_weights, + label_confidences=new_label_confidences, + multiclass_scores=new_multiclass_scores, + masks=new_masks, + keypoints=new_keypoints, + aspect_ratio=aspect_ratio, + clip_boxes=clip_boxes, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + return result + + +def ssd_random_crop_pad_fixed_aspect_ratio( + image, + boxes, + labels, + label_weights, + label_confidences=None, + multiclass_scores=None, + masks=None, + keypoints=None, + min_object_covered=(0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + aspect_ratio=1.0, + aspect_ratio_range=((0.5, 2.0),) * 7, + area_range=((0.1, 1.0),) * 7, + overlap_thresh=(0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0), + clip_boxes=(True,) * 7, + random_coef=(0.15,) * 7, + min_padded_size_ratio=(1.0, 1.0), + max_padded_size_ratio=(2.0, 2.0), + seed=None, + preprocess_vars_cache=None): + """Random crop and pad preprocessing with default parameters as in SSD paper. + + Liu et al., SSD: Single shot multibox detector. + For further information on random crop preprocessing refer to RandomCrop + function above. + + The only difference is that after the initial crop, images are zero-padded + to a fixed aspect ratio instead of being resized to that aspect ratio. + + Args: + image: rank 3 float32 tensor contains 1 image -> [height, width, channels] + with pixel values varying between [0, 1]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. + Each row is in the form of [ymin, xmin, ymax, xmax]. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: (optional) float32 tensor of shape [num_instances] + representing the confidence for each box. + multiclass_scores: (optional) float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x + normalized coordinates. + min_object_covered: the cropped image must cover at least this fraction of + at least one of the input bounding boxes. + aspect_ratio: the final aspect ratio to pad to. + aspect_ratio_range: allowed range for aspect ratio of cropped image. + area_range: allowed range for area ratio between cropped image and the + original image. + overlap_thresh: minimum overlap thresh with new cropped + image to keep the box. + clip_boxes: whether to clip the boxes to the cropped image. + random_coef: a random coefficient that defines the chance of getting the + original image. If random_coef is 0, we will always get the + cropped image, and if it is 1.0, we will always get the + original image. + min_padded_size_ratio: min ratio of padded image height and width to the + input image's height and width. + max_padded_size_ratio: max ratio of padded image height and width to the + input image's height and width. + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + + If multiclass_scores, masks, or keypoints is not None, the function also + returns: + + multiclass_scores: rank 2 with shape [num_instances, num_classes] + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + keypoints: rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2] + """ + crop_result = ssd_random_crop( + image, + boxes, + labels, + label_weights=label_weights, + label_confidences=label_confidences, + multiclass_scores=multiclass_scores, + masks=masks, + keypoints=keypoints, + min_object_covered=min_object_covered, + aspect_ratio_range=aspect_ratio_range, + area_range=area_range, + overlap_thresh=overlap_thresh, + clip_boxes=clip_boxes, + random_coef=random_coef, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + i = 3 + new_image, new_boxes, new_labels = crop_result[:i] + new_label_weights = None + new_label_confidences = None + new_multiclass_scores = None + new_masks = None + new_keypoints = None + if label_weights is not None: + new_label_weights = crop_result[i] + i += 1 + if label_confidences is not None: + new_label_confidences = crop_result[i] + i += 1 + if multiclass_scores is not None: + new_multiclass_scores = crop_result[i] + i += 1 + if masks is not None: + new_masks = crop_result[i] + i += 1 + if keypoints is not None: + new_keypoints = crop_result[i] + + result = random_pad_to_aspect_ratio( + new_image, + new_boxes, + masks=new_masks, + keypoints=new_keypoints, + aspect_ratio=aspect_ratio, + min_padded_size_ratio=min_padded_size_ratio, + max_padded_size_ratio=max_padded_size_ratio, + seed=seed, + preprocess_vars_cache=preprocess_vars_cache) + + result = list(result) + i = 3 + result.insert(2, new_labels) + if new_label_weights is not None: + result.insert(i, new_label_weights) + i += 1 + if new_label_confidences is not None: + result.insert(i, new_label_confidences) + i += 1 + if multiclass_scores is not None: + result.insert(i, new_multiclass_scores) + result = tuple(result) + + return result + + +def convert_class_logits_to_softmax(multiclass_scores, temperature=1.0): + """Converts multiclass logits to softmax scores after applying temperature. + + Args: + multiclass_scores: float32 tensor of shape + [num_instances, num_classes] representing the score for each box for each + class. + temperature: Scale factor to use prior to applying softmax. Larger + temperatures give more uniform distruibutions after softmax. + + Returns: + multiclass_scores: float32 tensor of shape + [num_instances, num_classes] with scaling and softmax applied. + """ + + # Multiclass scores must be stored as logits. Apply temp and softmax. + multiclass_scores_scaled = tf.multiply( + multiclass_scores, 1.0 / temperature, name='scale_logits') + multiclass_scores = tf.nn.softmax(multiclass_scores_scaled, name='softmax') + + return multiclass_scores + + +def _get_crop_border(border, size): + border = tf.cast(border, tf.float32) + size = tf.cast(size, tf.float32) + + i = tf.ceil(tf.log(2.0 * border / size) / tf.log(2.0)) + divisor = tf.pow(2.0, i) + divisor = tf.clip_by_value(divisor, 1, border) + divisor = tf.cast(divisor, tf.int32) + + return tf.cast(border, tf.int32) // divisor + + +def random_square_crop_by_scale(image, boxes, labels, label_weights, + label_confidences=None, masks=None, + keypoints=None, max_border=128, scale_min=0.6, + scale_max=1.3, num_scales=8, seed=None, + preprocess_vars_cache=None): + """Randomly crop a square in proportion to scale and image size. + + Extract a square sized crop from an image whose side length is sampled by + randomly scaling the maximum spatial dimension of the image. If part of + the crop falls outside the image, it is filled with zeros. + The augmentation is borrowed from [1] + [1]: https://arxiv.org/abs/1904.07850 + + Args: + image: rank 3 float32 tensor containing 1 image -> + [height, width, channels]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning their coordinates vary + between [0, 1]. Each row is in the form of [ymin, xmin, ymax, xmax]. + Boxes on the crop boundary are clipped to the boundary and boxes + falling outside the crop are ignored. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + label_confidences: (optional) float32 tensor of shape [num_instances] + representing the confidence for each box. + masks: (optional) rank 3 float32 tensor with shape + [num_instances, height, width] containing instance masks. The masks + are of the same height, width as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape + [num_instances, num_keypoints, 2]. The keypoints are in y-x normalized + coordinates. + max_border: The maximum size of the border. The border defines distance in + pixels to the image boundaries that will not be considered as a center of + a crop. To make sure that the border does not go over the center of the + image, we chose the border value by computing the minimum k, such that + (max_border / (2**k)) < image_dimension/2. + scale_min: float, the minimum value for scale. + scale_max: float, the maximum value for scale. + num_scales: int, the number of discrete scale values to sample between + [scale_min, scale_max] + seed: random seed. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + label_weights: rank 1 float32 tensor with shape [num_instances]. + label_confidences: (optional) float32 tensor of shape [num_instances] + representing the confidence for each box. + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + + """ + + img_shape = tf.shape(image) + height, width = img_shape[0], img_shape[1] + scales = tf.linspace(scale_min, scale_max, num_scales) + + scale = _get_or_create_preprocess_rand_vars( + lambda: scales[_random_integer(0, num_scales, seed)], + preprocessor_cache.PreprocessorCache.SQUARE_CROP_BY_SCALE, + preprocess_vars_cache, 'scale') + + image_size = scale * tf.cast(tf.maximum(height, width), tf.float32) + image_size = tf.cast(image_size, tf.int32) + h_border = _get_crop_border(max_border, height) + w_border = _get_crop_border(max_border, width) + + def y_function(): + y = _random_integer(h_border, + tf.cast(height, tf.int32) - h_border + 1, + seed) + return y + + def x_function(): + x = _random_integer(w_border, + tf.cast(width, tf.int32) - w_border + 1, + seed) + return x + + y_center = _get_or_create_preprocess_rand_vars( + y_function, + preprocessor_cache.PreprocessorCache.SQUARE_CROP_BY_SCALE, + preprocess_vars_cache, 'y_center') + + x_center = _get_or_create_preprocess_rand_vars( + x_function, + preprocessor_cache.PreprocessorCache.SQUARE_CROP_BY_SCALE, + preprocess_vars_cache, 'x_center') + + half_size = tf.cast(image_size / 2, tf.int32) + crop_ymin, crop_ymax = y_center - half_size, y_center + half_size + crop_xmin, crop_xmax = x_center - half_size, x_center + half_size + + ymin = tf.maximum(crop_ymin, 0) + xmin = tf.maximum(crop_xmin, 0) + ymax = tf.minimum(crop_ymax, height - 1) + xmax = tf.minimum(crop_xmax, width - 1) + + cropped_image = image[ymin:ymax, xmin:xmax] + offset_y = tf.maximum(0, ymin - crop_ymin) + offset_x = tf.maximum(0, xmin - crop_xmin) + + oy_i = offset_y + ox_i = offset_x + + output_image = tf.image.pad_to_bounding_box( + cropped_image, offset_height=oy_i, offset_width=ox_i, + target_height=image_size, target_width=image_size) + + if ymin == 0: + # We might be padding the image. + box_ymin = -offset_y + else: + box_ymin = crop_ymin + + if xmin == 0: + # We might be padding the image. + box_xmin = -offset_x + else: + box_xmin = crop_xmin + + box_ymax = box_ymin + image_size + box_xmax = box_xmin + image_size + + image_box = [box_ymin / height, box_xmin / width, + box_ymax / height, box_xmax / width] + boxlist = box_list.BoxList(boxes) + boxlist = box_list_ops.change_coordinate_frame(boxlist, image_box) + boxlist, indices = box_list_ops.prune_completely_outside_window( + boxlist, [0.0, 0.0, 1.0, 1.0]) + boxlist = box_list_ops.clip_to_window(boxlist, [0.0, 0.0, 1.0, 1.0], + filter_nonoverlapping=False) + + return_values = [output_image, boxlist.get(), + tf.gather(labels, indices), + tf.gather(label_weights, indices)] + + if label_confidences is not None: + return_values.append(tf.gather(label_confidences, indices)) + + if masks is not None: + new_masks = tf.expand_dims(masks, -1) + new_masks = new_masks[:, ymin:ymax, xmin:xmax] + new_masks = tf.image.pad_to_bounding_box( + new_masks, oy_i, ox_i, image_size, image_size) + new_masks = tf.squeeze(new_masks, [-1]) + return_values.append(tf.gather(new_masks, indices)) + + if keypoints is not None: + keypoints = tf.gather(keypoints, indices) + keypoints = keypoint_ops.change_coordinate_frame(keypoints, image_box) + keypoints = keypoint_ops.prune_outside_window(keypoints, + [0.0, 0.0, 1.0, 1.0]) + return_values.append(keypoints) + + return return_values + + +def random_scale_crop_and_pad_to_square( + image, + boxes, + labels, + label_weights, + masks=None, + keypoints=None, + label_confidences=None, + scale_min=0.1, + scale_max=2.0, + output_size=512, + resize_method=tf.image.ResizeMethod.BILINEAR, + seed=None): + """Randomly scale, crop, and then pad an image to fixed square dimensions. + + Randomly scale, crop, and then pad an image to the desired square output + dimensions. Specifically, this method first samples a random_scale factor + from a uniform distribution between scale_min and scale_max, and then resizes + the image such that it's maximum dimension is (output_size * random_scale). + Secondly, a square output_size crop is extracted from the resized image + (note, this will only occur when random_scale > 1.0). Lastly, the cropped + region is padded to the desired square output_size, by filling with zeros. + The augmentation is borrowed from [1] + [1]: https://arxiv.org/abs/1911.09070 + + Args: + image: rank 3 float32 tensor containing 1 image -> + [height, width, channels]. + boxes: rank 2 float32 tensor containing the bounding boxes -> [N, 4]. Boxes + are in normalized form meaning their coordinates vary between [0, 1]. Each + row is in the form of [ymin, xmin, ymax, xmax]. Boxes on the crop boundary + are clipped to the boundary and boxes falling outside the crop are + ignored. + labels: rank 1 int32 tensor containing the object classes. + label_weights: float32 tensor of shape [num_instances] representing the + weight for each box. + masks: (optional) rank 3 float32 tensor with shape [num_instances, height, + width] containing instance masks. The masks are of the same height, width + as the input `image`. + keypoints: (optional) rank 3 float32 tensor with shape [num_instances, + num_keypoints, 2]. The keypoints are in y-x normalized coordinates. + label_confidences: (optional) float32 tensor of shape [num_instance] + representing the confidence for each box. + scale_min: float, the minimum value for the random scale factor. + scale_max: float, the maximum value for the random scale factor. + output_size: int, the desired (square) output image size. + resize_method: tf.image.ResizeMethod, resize method to use when scaling the + input images. + seed: random seed. + + Returns: + image: image which is the same rank as input image. + boxes: boxes which is the same rank as input boxes. + Boxes are in normalized form. + labels: new labels. + label_weights: rank 1 float32 tensor with shape [num_instances]. + masks: rank 3 float32 tensor with shape [num_instances, height, width] + containing instance masks. + label_confidences: confidences for retained boxes. + """ + img_shape = tf.shape(image) + input_height, input_width = img_shape[0], img_shape[1] + random_scale = tf.random_uniform([], scale_min, scale_max, seed=seed) + + # Compute the scaled height and width from the random scale. + max_input_dim = tf.cast(tf.maximum(input_height, input_width), tf.float32) + input_ar_y = tf.cast(input_height, tf.float32) / max_input_dim + input_ar_x = tf.cast(input_width, tf.float32) / max_input_dim + scaled_height = tf.cast(random_scale * output_size * input_ar_y, tf.int32) + scaled_width = tf.cast(random_scale * output_size * input_ar_x, tf.int32) + + # Compute the offsets: + offset_y = tf.cast(scaled_height - output_size, tf.float32) + offset_x = tf.cast(scaled_width - output_size, tf.float32) + offset_y = tf.maximum(0.0, offset_y) * tf.random_uniform([], 0, 1, seed=seed) + offset_x = tf.maximum(0.0, offset_x) * tf.random_uniform([], 0, 1, seed=seed) + offset_y = tf.cast(offset_y, tf.int32) + offset_x = tf.cast(offset_x, tf.int32) + + # Scale, crop, and pad the input image. + scaled_image = tf.image.resize_images( + image, [scaled_height, scaled_width], method=resize_method) + scaled_image = scaled_image[offset_y:offset_y + output_size, + offset_x:offset_x + output_size, :] + output_image = tf.image.pad_to_bounding_box(scaled_image, 0, 0, output_size, + output_size) + + # Update the boxes. + new_window = tf.cast( + tf.stack([offset_y, offset_x, + offset_y + output_size, offset_x + output_size]), + dtype=tf.float32) + new_window /= tf.cast( + tf.stack([scaled_height, scaled_width, scaled_height, scaled_width]), + dtype=tf.float32) + boxlist = box_list.BoxList(boxes) + boxlist = box_list_ops.change_coordinate_frame(boxlist, new_window) + boxlist, indices = box_list_ops.prune_completely_outside_window( + boxlist, [0.0, 0.0, 1.0, 1.0]) + boxlist = box_list_ops.clip_to_window( + boxlist, [0.0, 0.0, 1.0, 1.0], filter_nonoverlapping=False) + + return_values = [output_image, boxlist.get(), + tf.gather(labels, indices), + tf.gather(label_weights, indices)] + + if masks is not None: + new_masks = tf.expand_dims(masks, -1) + new_masks = tf.image.resize_images( + new_masks, [scaled_height, scaled_width], method=resize_method) + new_masks = new_masks[:, offset_y:offset_y + output_size, + offset_x:offset_x + output_size, :] + new_masks = tf.image.pad_to_bounding_box( + new_masks, 0, 0, output_size, output_size) + new_masks = tf.squeeze(new_masks, [-1]) + return_values.append(tf.gather(new_masks, indices)) + + if keypoints is not None: + keypoints = tf.gather(keypoints, indices) + keypoints = keypoint_ops.change_coordinate_frame(keypoints, new_window) + keypoints = keypoint_ops.prune_outside_window( + keypoints, [0.0, 0.0, 1.0, 1.0]) + return_values.append(keypoints) + + if label_confidences is not None: + return_values.append(tf.gather(label_confidences, indices)) + + return return_values + + +def get_default_func_arg_map(include_label_weights=True, + include_label_confidences=False, + include_multiclass_scores=False, + include_instance_masks=False, + include_keypoints=False, + include_keypoint_visibilities=False, + include_dense_pose=False): + """Returns the default mapping from a preprocessor function to its args. + + Args: + include_label_weights: If True, preprocessing functions will modify the + label weights, too. + include_label_confidences: If True, preprocessing functions will modify the + label confidences, too. + include_multiclass_scores: If True, preprocessing functions will modify the + multiclass scores, too. + include_instance_masks: If True, preprocessing functions will modify the + instance masks, too. + include_keypoints: If True, preprocessing functions will modify the + keypoints, too. + include_keypoint_visibilities: If True, preprocessing functions will modify + the keypoint visibilities, too. + include_dense_pose: If True, preprocessing functions will modify the + DensePose labels, too. + + Returns: + A map from preprocessing functions to the arguments they receive. + """ + groundtruth_label_weights = None + if include_label_weights: + groundtruth_label_weights = ( + fields.InputDataFields.groundtruth_weights) + + groundtruth_label_confidences = None + if include_label_confidences: + groundtruth_label_confidences = ( + fields.InputDataFields.groundtruth_confidences) + + multiclass_scores = None + if include_multiclass_scores: + multiclass_scores = (fields.InputDataFields.multiclass_scores) + + groundtruth_instance_masks = None + if include_instance_masks: + groundtruth_instance_masks = ( + fields.InputDataFields.groundtruth_instance_masks) + + groundtruth_keypoints = None + if include_keypoints: + groundtruth_keypoints = fields.InputDataFields.groundtruth_keypoints + + groundtruth_keypoint_visibilities = None + if include_keypoint_visibilities: + groundtruth_keypoint_visibilities = ( + fields.InputDataFields.groundtruth_keypoint_visibilities) + + groundtruth_dp_num_points = None + groundtruth_dp_part_ids = None + groundtruth_dp_surface_coords = None + if include_dense_pose: + groundtruth_dp_num_points = ( + fields.InputDataFields.groundtruth_dp_num_points) + groundtruth_dp_part_ids = ( + fields.InputDataFields.groundtruth_dp_part_ids) + groundtruth_dp_surface_coords = ( + fields.InputDataFields.groundtruth_dp_surface_coords) + + prep_func_arg_map = { + normalize_image: (fields.InputDataFields.image,), + random_horizontal_flip: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + groundtruth_instance_masks, + groundtruth_keypoints, + groundtruth_keypoint_visibilities, + groundtruth_dp_part_ids, + groundtruth_dp_surface_coords, + ), + random_vertical_flip: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + random_rotation90: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + random_pixel_value_scale: (fields.InputDataFields.image,), + random_image_scale: ( + fields.InputDataFields.image, + groundtruth_instance_masks, + ), + random_rgb_to_gray: (fields.InputDataFields.image,), + random_adjust_brightness: (fields.InputDataFields.image,), + random_adjust_contrast: (fields.InputDataFields.image,), + random_adjust_hue: (fields.InputDataFields.image,), + random_adjust_saturation: (fields.InputDataFields.image,), + random_distort_color: (fields.InputDataFields.image,), + random_jitter_boxes: (fields.InputDataFields.groundtruth_boxes,), + random_crop_image: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, groundtruth_label_confidences, + multiclass_scores, groundtruth_instance_masks, groundtruth_keypoints, + groundtruth_keypoint_visibilities, groundtruth_dp_num_points, + groundtruth_dp_part_ids, groundtruth_dp_surface_coords), + random_pad_image: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, groundtruth_instance_masks, + groundtruth_keypoints, groundtruth_dp_surface_coords), + random_absolute_pad_image: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, groundtruth_instance_masks, + groundtruth_keypoints, groundtruth_dp_surface_coords), + random_crop_pad_image: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, multiclass_scores), + random_crop_to_aspect_ratio: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, + multiclass_scores, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + random_pad_to_aspect_ratio: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + random_black_patches: (fields.InputDataFields.image,), + random_jpeg_quality: (fields.InputDataFields.image,), + random_downscale_to_target_pixels: ( + fields.InputDataFields.image, + groundtruth_instance_masks, + ), + random_patch_gaussian: (fields.InputDataFields.image,), + autoaugment_image: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + ), + retain_boxes_above_threshold: ( + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, + multiclass_scores, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + drop_label_probabilistically: ( + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, + multiclass_scores, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + remap_labels: (fields.InputDataFields.groundtruth_classes,), + image_to_float: (fields.InputDataFields.image,), + random_resize_method: (fields.InputDataFields.image,), + resize_to_range: ( + fields.InputDataFields.image, + groundtruth_instance_masks, + ), + resize_to_min_dimension: ( + fields.InputDataFields.image, + groundtruth_instance_masks, + ), + scale_boxes_to_pixel_coordinates: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + groundtruth_keypoints, + ), + resize_image: ( + fields.InputDataFields.image, + groundtruth_instance_masks, + ), + subtract_channel_mean: (fields.InputDataFields.image,), + one_hot_encoding: (fields.InputDataFields.groundtruth_image_classes,), + rgb_to_gray: (fields.InputDataFields.image,), + random_self_concat_image: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, groundtruth_label_confidences, + multiclass_scores), + ssd_random_crop: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, multiclass_scores, + groundtruth_instance_masks, groundtruth_keypoints), + ssd_random_crop_pad: (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, multiclass_scores), + ssd_random_crop_fixed_aspect_ratio: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, groundtruth_label_confidences, + multiclass_scores, groundtruth_instance_masks, groundtruth_keypoints + ), + ssd_random_crop_pad_fixed_aspect_ratio: ( + fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, + groundtruth_label_confidences, + multiclass_scores, + groundtruth_instance_masks, + groundtruth_keypoints, + ), + convert_class_logits_to_softmax: (multiclass_scores,), + random_square_crop_by_scale: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, groundtruth_label_confidences, + groundtruth_instance_masks, groundtruth_keypoints), + random_scale_crop_and_pad_to_square: + (fields.InputDataFields.image, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_classes, + groundtruth_label_weights, groundtruth_instance_masks, + groundtruth_keypoints, groundtruth_label_confidences), + } + + return prep_func_arg_map + + +def preprocess(tensor_dict, + preprocess_options, + func_arg_map=None, + preprocess_vars_cache=None): + """Preprocess images and bounding boxes. + + Various types of preprocessing (to be implemented) based on the + preprocess_options dictionary e.g. "crop image" (affects image and possibly + boxes), "white balance image" (affects only image), etc. If self._options + is None, no preprocessing is done. + + Args: + tensor_dict: dictionary that contains images, boxes, and can contain other + things as well. + images-> rank 4 float32 tensor contains + 1 image -> [1, height, width, 3]. + with pixel values varying between [0, 1] + boxes-> rank 2 float32 tensor containing + the bounding boxes -> [N, 4]. + Boxes are in normalized form meaning + their coordinates vary between [0, 1]. + Each row is in the form + of [ymin, xmin, ymax, xmax]. + preprocess_options: It is a list of tuples, where each tuple contains a + function and a dictionary that contains arguments and + their values. + func_arg_map: mapping from preprocessing functions to arguments that they + expect to receive and return. + preprocess_vars_cache: PreprocessorCache object that records previously + performed augmentations. Updated in-place. If this + function is called multiple times with the same + non-null cache, it will perform deterministically. + + Returns: + tensor_dict: which contains the preprocessed images, bounding boxes, etc. + + Raises: + ValueError: (a) If the functions passed to Preprocess + are not in func_arg_map. + (b) If the arguments that a function needs + do not exist in tensor_dict. + (c) If image in tensor_dict is not rank 4 + """ + if func_arg_map is None: + func_arg_map = get_default_func_arg_map() + # changes the images to image (rank 4 to rank 3) since the functions + # receive rank 3 tensor for image + if fields.InputDataFields.image in tensor_dict: + images = tensor_dict[fields.InputDataFields.image] + if len(images.get_shape()) != 4: + raise ValueError('images in tensor_dict should be rank 4') + image = tf.squeeze(images, axis=0) + tensor_dict[fields.InputDataFields.image] = image + + # Preprocess inputs based on preprocess_options + for option in preprocess_options: + func, params = option + if func not in func_arg_map: + raise ValueError('The function %s does not exist in func_arg_map' % + (func.__name__)) + arg_names = func_arg_map[func] + for a in arg_names: + if a is not None and a not in tensor_dict: + raise ValueError('The function %s requires argument %s' % + (func.__name__, a)) + + def get_arg(key): + return tensor_dict[key] if key is not None else None + + args = [get_arg(a) for a in arg_names] + if preprocess_vars_cache is not None: + if six.PY2: + # pylint: disable=deprecated-method + arg_spec = inspect.getargspec(func) + # pylint: enable=deprecated-method + else: + arg_spec = inspect.getfullargspec(func) + if 'preprocess_vars_cache' in arg_spec.args: + params['preprocess_vars_cache'] = preprocess_vars_cache + + results = func(*args, **params) + if not isinstance(results, (list, tuple)): + results = (results,) + # Removes None args since the return values will not contain those. + arg_names = [arg_name for arg_name in arg_names if arg_name is not None] + for res, arg_name in zip(results, arg_names): + tensor_dict[arg_name] = res + + # changes the image to images (rank 3 to rank 4) to be compatible to what + # we received in the first place + if fields.InputDataFields.image in tensor_dict: + image = tensor_dict[fields.InputDataFields.image] + images = tf.expand_dims(image, 0) + tensor_dict[fields.InputDataFields.image] = images + + return tensor_dict diff --git a/workspace/virtuallab/object_detection/core/preprocessor.pyc b/workspace/virtuallab/object_detection/core/preprocessor.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e5d824eb934806a22ddfb62fed04c9460cf5c42b GIT binary patch literal 170622 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%0D+3=0w`eihjBpH+W2zPWSp8QVj@wQ|s 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 HXyE??{pk;> literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/preprocessor_cache.py b/workspace/virtuallab/object_detection/core/preprocessor_cache.py new file mode 100644 index 0000000..9487105 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/preprocessor_cache.py @@ -0,0 +1,109 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Records previous preprocessing operations and allows them to be repeated. + +Used with object_detection.core.preprocessor. Passing a PreprocessorCache +into individual data augmentation functions or the general preprocess() function +will store all randomly generated variables in the PreprocessorCache. When +a preprocessor function is called multiple times with the same +PreprocessorCache object, that function will perform the same augmentation +on all calls. +""" + +import collections + + +class PreprocessorCache(object): + """Dictionary wrapper storing random variables generated during preprocessing. + """ + + # Constant keys representing different preprocessing functions + ROTATION90 = 'rotation90' + HORIZONTAL_FLIP = 'horizontal_flip' + VERTICAL_FLIP = 'vertical_flip' + PIXEL_VALUE_SCALE = 'pixel_value_scale' + IMAGE_SCALE = 'image_scale' + RGB_TO_GRAY = 'rgb_to_gray' + ADJUST_BRIGHTNESS = 'adjust_brightness' + ADJUST_CONTRAST = 'adjust_contrast' + ADJUST_HUE = 'adjust_hue' + ADJUST_SATURATION = 'adjust_saturation' + DISTORT_COLOR = 'distort_color' + STRICT_CROP_IMAGE = 'strict_crop_image' + CROP_IMAGE = 'crop_image' + PAD_IMAGE = 'pad_image' + CROP_TO_ASPECT_RATIO = 'crop_to_aspect_ratio' + RESIZE_METHOD = 'resize_method' + PAD_TO_ASPECT_RATIO = 'pad_to_aspect_ratio' + BLACK_PATCHES = 'black_patches' + ADD_BLACK_PATCH = 'add_black_patch' + SELECTOR = 'selector' + SELECTOR_TUPLES = 'selector_tuples' + SELF_CONCAT_IMAGE = 'self_concat_image' + SSD_CROP_SELECTOR_ID = 'ssd_crop_selector_id' + SSD_CROP_PAD_SELECTOR_ID = 'ssd_crop_pad_selector_id' + JPEG_QUALITY = 'jpeg_quality' + DOWNSCALE_TO_TARGET_PIXELS = 'downscale_to_target_pixels' + PATCH_GAUSSIAN = 'patch_gaussian' + SQUARE_CROP_BY_SCALE = 'square_crop_scale' + + # 27 permitted function ids + _VALID_FNS = [ROTATION90, HORIZONTAL_FLIP, VERTICAL_FLIP, PIXEL_VALUE_SCALE, + IMAGE_SCALE, RGB_TO_GRAY, ADJUST_BRIGHTNESS, ADJUST_CONTRAST, + ADJUST_HUE, ADJUST_SATURATION, DISTORT_COLOR, STRICT_CROP_IMAGE, + CROP_IMAGE, PAD_IMAGE, CROP_TO_ASPECT_RATIO, RESIZE_METHOD, + PAD_TO_ASPECT_RATIO, BLACK_PATCHES, ADD_BLACK_PATCH, SELECTOR, + SELECTOR_TUPLES, SELF_CONCAT_IMAGE, SSD_CROP_SELECTOR_ID, + SSD_CROP_PAD_SELECTOR_ID, JPEG_QUALITY, + DOWNSCALE_TO_TARGET_PIXELS, PATCH_GAUSSIAN, + SQUARE_CROP_BY_SCALE] + + def __init__(self): + self._history = collections.defaultdict(dict) + + def clear(self): + """Resets cache.""" + self._history = collections.defaultdict(dict) + + def get(self, function_id, key): + """Gets stored value given a function id and key. + + Args: + function_id: identifier for the preprocessing function used. + key: identifier for the variable stored. + Returns: + value: the corresponding value, expected to be a tensor or + nested structure of tensors. + Raises: + ValueError: if function_id is not one of the 23 valid function ids. + """ + if function_id not in self._VALID_FNS: + raise ValueError('Function id not recognized: %s.' % str(function_id)) + return self._history[function_id].get(key) + + def update(self, function_id, key, value): + """Adds a value to the dictionary. + + Args: + function_id: identifier for the preprocessing function used. + key: identifier for the variable stored. + value: the value to store, expected to be a tensor or nested structure + of tensors. + Raises: + ValueError: if function_id is not one of the 23 valid function ids. + """ + if function_id not in self._VALID_FNS: + raise ValueError('Function id not recognized: %s.' % str(function_id)) + self._history[function_id][key] = value diff --git a/workspace/virtuallab/object_detection/core/preprocessor_cache.pyc b/workspace/virtuallab/object_detection/core/preprocessor_cache.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1f0acfe12c42a6afff05987f823a5836163584d GIT binary patch literal 4242 zcmd5;?{XVS5pUU!9m{r{zfPPu$=n?Wz9J=4z!y@1LbeoJhffl_>m1oCY8Y!;(mJbM zu{*NQS1CSleC7do3m${#;Q^q#cO~mcin@Y)IJtH_f4hG@{hRJy{O3mX?|=Q9?m_um zr2oIBFMmdj1qi9FfL(yN0wHx40T-Z8ohA4Sz&8M|!$n^9cdzlOe^dZG?4_B_)i}fR zD4j@WoTWX?^C%gpbc~sCQJUn+BvzR?PA_uhhB#6#Ri~&j9HVh)Yt`yWj#gbnZm81J zGweBS(a}FTTq7M=BNe6OG^?quDKr?>otr-}y&+bkgoH`(DkEFcQG8jRBKYduWRW?IF(GJz&Mps>){?;8-(?pD6t$D2){fmBWcZ(eRj*b~&c4&2;{CJU~fQp z6YM60x4_|F@Az}|!KKG-b?AAr3N;WpR@5IzLE4dElO z4AiohVSY{ZYt>0TPsKZcdNj4nv`vUIEk zj0l?>t}=FG%f-7il0^b(@-asb5iim+%%iVSkI)TM>+X~X-?#HLHoZ4`Y#c?AsYzDP z&ARK14P#P3K>3!Z1MMdCX7d6(#(lCS=@~bNFVAgZ&+JHK-2)aeU0{3W7uRX=Y>Wf_ zM~Y&RyL1m}!loCAIEo$OOg2EL#fiDQLCXSO56pzZqe%)lbUedSH@mn;uNU}Rpznab ze2W@K$|~R%;F}7(1<(}gfkHj+Yws@c24)7ih^^<4E@O**LjlW@w;XSh4&RaE| zfGm+|{asoCY4R7;+Z(1M+&fE8_eQD3IN$rv(7(r_fA5O#X{LJ|<7?wf@rBk=5;?6~ zBrIohpz@uqtB`ekBT{`gkKNqk{%#W!aFZ&o1N1xEo`d`9!+wk`VpT!Do4kuw*0_x9NmycM68#{P*M zddQ|qKI6Yv>NpzieQv& zB-vc~i=)*n$4w~1bF|_g34ZU@wCBy}oH3K^7dfBG{a8*Lb{STg6%s0zwJsizrra`#dALFww6WyN&V@mgW09n_EY z%i~s8iicNCFK8W1XQVuC{jPb;ukxg+eOhVCOKW{mKb#+XhxdKMb1%u3_CYwm6C{2= padded_images_shape_[1] * 0.5).all) + self.assertTrue((images_shape_[2] >= padded_images_shape_[2] * 0.5).all) + self.assertTrue((images_shape_[1] <= padded_images_shape_[1]).all) + self.assertTrue((images_shape_[2] <= padded_images_shape_[2]).all) + self.assertTrue(np.all((boxes_[:, 2] - boxes_[:, 0]) >= ( + padded_boxes_[:, 2] - padded_boxes_[:, 0]))) + self.assertTrue(np.all((boxes_[:, 3] - boxes_[:, 1]) >= ( + padded_boxes_[:, 3] - padded_boxes_[:, 1]))) + + @parameterized.parameters( + {'include_dense_pose': False}, + ) + def testRandomPadImageWithKeypointsAndMasks(self, include_dense_pose): + def graph_fn(): + preprocessing_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })] + + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + masks = self.createTestMasks() + keypoints, _ = self.createTestKeypoints() + _, _, dp_surface_coords = self.createTestDensePose() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_instance_masks: masks, + fields.InputDataFields.groundtruth_keypoints: keypoints, + fields.InputDataFields.groundtruth_dp_surface_coords: + dp_surface_coords + } + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_pad_image, {})] + func_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True, + include_keypoints=True, + include_keypoint_visibilities=True, + include_dense_pose=include_dense_pose) + padded_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options, + func_arg_map=func_arg_map) + + padded_images = padded_tensor_dict[fields.InputDataFields.image] + padded_boxes = padded_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + padded_masks = padded_tensor_dict[ + fields.InputDataFields.groundtruth_instance_masks] + padded_keypoints = padded_tensor_dict[ + fields.InputDataFields.groundtruth_keypoints] + boxes_shape = tf.shape(boxes) + padded_boxes_shape = tf.shape(padded_boxes) + padded_masks_shape = tf.shape(padded_masks) + keypoints_shape = tf.shape(keypoints) + padded_keypoints_shape = tf.shape(padded_keypoints) + images_shape = tf.shape(images) + padded_images_shape = tf.shape(padded_images) + outputs = [boxes_shape, padded_boxes_shape, padded_masks_shape, + keypoints_shape, padded_keypoints_shape, images_shape, + padded_images_shape, boxes, padded_boxes, keypoints, + padded_keypoints] + if include_dense_pose: + padded_dp_surface_coords = padded_tensor_dict[ + fields.InputDataFields.groundtruth_dp_surface_coords] + outputs.extend([dp_surface_coords, padded_dp_surface_coords]) + return outputs + + outputs = self.execute_cpu(graph_fn, []) + boxes_shape_ = outputs[0] + padded_boxes_shape_ = outputs[1] + padded_masks_shape_ = outputs[2] + keypoints_shape_ = outputs[3] + padded_keypoints_shape_ = outputs[4] + images_shape_ = outputs[5] + padded_images_shape_ = outputs[6] + boxes_ = outputs[7] + padded_boxes_ = outputs[8] + keypoints_ = outputs[9] + padded_keypoints_ = outputs[10] + + self.assertAllEqual(boxes_shape_, padded_boxes_shape_) + self.assertAllEqual(keypoints_shape_, padded_keypoints_shape_) + self.assertTrue((images_shape_[1] >= padded_images_shape_[1] * 0.5).all) + self.assertTrue((images_shape_[2] >= padded_images_shape_[2] * 0.5).all) + self.assertTrue((images_shape_[1] <= padded_images_shape_[1]).all) + self.assertTrue((images_shape_[2] <= padded_images_shape_[2]).all) + self.assertAllEqual(padded_masks_shape_[1:3], padded_images_shape_[1:3]) + self.assertTrue(np.all((boxes_[:, 2] - boxes_[:, 0]) >= ( + padded_boxes_[:, 2] - padded_boxes_[:, 0]))) + self.assertTrue(np.all((boxes_[:, 3] - boxes_[:, 1]) >= ( + padded_boxes_[:, 3] - padded_boxes_[:, 1]))) + self.assertTrue(np.all((keypoints_[1, :, 0] - keypoints_[0, :, 0]) >= ( + padded_keypoints_[1, :, 0] - padded_keypoints_[0, :, 0]))) + self.assertTrue(np.all((keypoints_[1, :, 1] - keypoints_[0, :, 1]) >= ( + padded_keypoints_[1, :, 1] - padded_keypoints_[0, :, 1]))) + if include_dense_pose: + dp_surface_coords = outputs[11] + padded_dp_surface_coords = outputs[12] + self.assertAllClose(padded_dp_surface_coords[:, :, 2:], + dp_surface_coords[:, :, 2:]) + + def testRandomAbsolutePadImage(self): + height_padding = 10 + width_padding = 20 + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + tensor_dict = { + fields.InputDataFields.image: tf.cast(images, dtype=tf.float32), + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + } + preprocessing_options = [(preprocessor.random_absolute_pad_image, { + 'max_height_padding': height_padding, + 'max_width_padding': width_padding})] + padded_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + original_shape = tf.shape(images) + final_shape = tf.shape(padded_tensor_dict[fields.InputDataFields.image]) + return original_shape, final_shape + for _ in range(100): + original_shape, output_shape = self.execute_cpu(graph_fn, []) + _, height, width, _ = original_shape + self.assertGreaterEqual(output_shape[1], height) + self.assertLess(output_shape[1], height + height_padding) + self.assertGreaterEqual(output_shape[2], width) + self.assertLess(output_shape[2], width + width_padding) + + def testRandomAbsolutePadImageWithKeypoints(self): + height_padding = 10 + width_padding = 20 + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + keypoints, _ = self.createTestKeypoints() + tensor_dict = { + fields.InputDataFields.image: tf.cast(images, dtype=tf.float32), + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_keypoints: keypoints, + } + + preprocessing_options = [(preprocessor.random_absolute_pad_image, { + 'max_height_padding': height_padding, + 'max_width_padding': width_padding + })] + padded_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + original_shape = tf.shape(images) + final_shape = tf.shape(padded_tensor_dict[fields.InputDataFields.image]) + padded_keypoints = padded_tensor_dict[ + fields.InputDataFields.groundtruth_keypoints] + return (original_shape, final_shape, padded_keypoints) + for _ in range(100): + original_shape, output_shape, padded_keypoints_ = self.execute_cpu( + graph_fn, []) + _, height, width, _ = original_shape + self.assertGreaterEqual(output_shape[1], height) + self.assertLess(output_shape[1], height + height_padding) + self.assertGreaterEqual(output_shape[2], width) + self.assertLess(output_shape[2], width + width_padding) + # Verify the keypoints are populated. The correctness of the keypoint + # coordinates are already tested in random_pad_image function. + self.assertEqual(padded_keypoints_.shape, (2, 3, 2)) + + def testRandomCropPadImageWithCache(self): + preprocess_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1,}), (preprocessor.random_crop_pad_image, {})] + self._testPreprocessorCache(preprocess_options, + test_boxes=True, + test_masks=True, + test_keypoints=True) + + def testRandomCropPadImageWithRandomCoefOne(self): + def graph_fn(): + preprocessing_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })] + + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights, + } + tensor_dict = preprocessor.preprocess(tensor_dict, preprocessing_options) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_crop_pad_image, { + 'random_coef': 1.0 + })] + padded_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + + padded_images = padded_tensor_dict[fields.InputDataFields.image] + padded_boxes = padded_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + boxes_shape = tf.shape(boxes) + padded_boxes_shape = tf.shape(padded_boxes) + images_shape = tf.shape(images) + padded_images_shape = tf.shape(padded_images) + return [boxes_shape, padded_boxes_shape, images_shape, + padded_images_shape, boxes, padded_boxes] + (boxes_shape_, padded_boxes_shape_, images_shape_, + padded_images_shape_, boxes_, padded_boxes_) = self.execute_cpu(graph_fn, + []) + self.assertAllEqual(boxes_shape_, padded_boxes_shape_) + self.assertTrue((images_shape_[1] >= padded_images_shape_[1] * 0.5).all) + self.assertTrue((images_shape_[2] >= padded_images_shape_[2] * 0.5).all) + self.assertTrue((images_shape_[1] <= padded_images_shape_[1]).all) + self.assertTrue((images_shape_[2] <= padded_images_shape_[2]).all) + self.assertTrue(np.all((boxes_[:, 2] - boxes_[:, 0]) >= ( + padded_boxes_[:, 2] - padded_boxes_[:, 0]))) + self.assertTrue(np.all((boxes_[:, 3] - boxes_[:, 1]) >= ( + padded_boxes_[:, 3] - padded_boxes_[:, 1]))) + + def testRandomCropToAspectRatio(self): + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights, + } + tensor_dict = preprocessor.preprocess(tensor_dict, []) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_crop_to_aspect_ratio, { + 'aspect_ratio': 2.0 + })] + cropped_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + + cropped_images = cropped_tensor_dict[fields.InputDataFields.image] + cropped_boxes = cropped_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + boxes_shape = tf.shape(boxes) + cropped_boxes_shape = tf.shape(cropped_boxes) + images_shape = tf.shape(images) + cropped_images_shape = tf.shape(cropped_images) + return [ + boxes_shape, cropped_boxes_shape, images_shape, cropped_images_shape + ] + + (boxes_shape_, cropped_boxes_shape_, images_shape_, + cropped_images_shape_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(boxes_shape_, cropped_boxes_shape_) + self.assertEqual(images_shape_[1], cropped_images_shape_[1] * 2) + self.assertEqual(images_shape_[2], cropped_images_shape_[2]) + + def testRandomPadToAspectRatio(self): + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + } + tensor_dict = preprocessor.preprocess(tensor_dict, []) + images = tensor_dict[fields.InputDataFields.image] + + preprocessing_options = [(preprocessor.random_pad_to_aspect_ratio, { + 'aspect_ratio': 2.0 + })] + padded_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + + padded_images = padded_tensor_dict[fields.InputDataFields.image] + padded_boxes = padded_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + boxes_shape = tf.shape(boxes) + padded_boxes_shape = tf.shape(padded_boxes) + images_shape = tf.shape(images) + padded_images_shape = tf.shape(padded_images) + return [ + boxes_shape, padded_boxes_shape, images_shape, padded_images_shape + ] + + (boxes_shape_, padded_boxes_shape_, images_shape_, + padded_images_shape_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(boxes_shape_, padded_boxes_shape_) + self.assertEqual(images_shape_[1], padded_images_shape_[1]) + self.assertEqual(2 * images_shape_[2], padded_images_shape_[2]) + + def testRandomBlackPatchesWithCache(self): + preprocess_options = [] + preprocess_options.append((preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })) + preprocess_options.append((preprocessor.random_black_patches, { + 'size_to_image_ratio': 0.5 + })) + self._testPreprocessorCache(preprocess_options, + test_boxes=True, + test_masks=True, + test_keypoints=True) + + def testRandomBlackPatches(self): + def graph_fn(): + preprocessing_options = [] + preprocessing_options.append((preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })) + preprocessing_options.append((preprocessor.random_black_patches, { + 'size_to_image_ratio': 0.5 + })) + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + blacked_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + blacked_images = blacked_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + blacked_images_shape = tf.shape(blacked_images) + return [images_shape, blacked_images_shape] + (images_shape_, blacked_images_shape_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape_, blacked_images_shape_) + + def testRandomJpegQuality(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_jpeg_quality, { + 'min_jpeg_quality': 0, + 'max_jpeg_quality': 100 + })] + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + encoded_images = processed_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + encoded_images_shape = tf.shape(encoded_images) + return [images_shape, encoded_images_shape] + images_shape_out, encoded_images_shape_out = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape_out, encoded_images_shape_out) + + def testRandomJpegQualityKeepsStaticChannelShape(self): + # Set at least three weeks past the forward compatibility horizon for + # tf 1.14 of 2019/11/01. + # https://github.com/tensorflow/tensorflow/blob/v1.14.0/tensorflow/python/compat/compat.py#L30 + if not tf.compat.forward_compatible(year=2019, month=12, day=1): + self.skipTest('Skipping test for future functionality.') + preprocessing_options = [(preprocessor.random_jpeg_quality, { + 'min_jpeg_quality': 0, + 'max_jpeg_quality': 100 + })] + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + encoded_images = processed_tensor_dict[fields.InputDataFields.image] + images_static_channels = images.shape[-1] + encoded_images_static_channels = encoded_images.shape[-1] + self.assertEqual(images_static_channels, encoded_images_static_channels) + + def testRandomJpegQualityWithCache(self): + preprocessing_options = [(preprocessor.random_jpeg_quality, { + 'min_jpeg_quality': 0, + 'max_jpeg_quality': 100 + })] + self._testPreprocessorCache(preprocessing_options) + + def testRandomJpegQualityWithRandomCoefOne(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_jpeg_quality, { + 'random_coef': 1.0 + })] + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + encoded_images = processed_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + encoded_images_shape = tf.shape(encoded_images) + return [images, encoded_images, images_shape, encoded_images_shape] + + (images_out, encoded_images_out, images_shape_out, + encoded_images_shape_out) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape_out, encoded_images_shape_out) + self.assertAllEqual(images_out, encoded_images_out) + + def testRandomDownscaleToTargetPixels(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_downscale_to_target_pixels, + { + 'min_target_pixels': 100, + 'max_target_pixels': 101 + })] + images = tf.random_uniform([1, 25, 100, 3]) + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + downscaled_images = processed_tensor_dict[fields.InputDataFields.image] + downscaled_shape = tf.shape(downscaled_images) + return downscaled_shape + expected_shape = [1, 5, 20, 3] + downscaled_shape_out = self.execute_cpu(graph_fn, []) + self.assertAllEqual(downscaled_shape_out, expected_shape) + + def testRandomDownscaleToTargetPixelsWithMasks(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_downscale_to_target_pixels, + { + 'min_target_pixels': 100, + 'max_target_pixels': 101 + })] + images = tf.random_uniform([1, 25, 100, 3]) + masks = tf.random_uniform([10, 25, 100]) + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_instance_masks: masks + } + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_instance_masks=True) + processed_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + downscaled_images = processed_tensor_dict[fields.InputDataFields.image] + downscaled_masks = processed_tensor_dict[ + fields.InputDataFields.groundtruth_instance_masks] + downscaled_images_shape = tf.shape(downscaled_images) + downscaled_masks_shape = tf.shape(downscaled_masks) + return [downscaled_images_shape, downscaled_masks_shape] + expected_images_shape = [1, 5, 20, 3] + expected_masks_shape = [10, 5, 20] + (downscaled_images_shape_out, + downscaled_masks_shape_out) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(downscaled_images_shape_out, expected_images_shape) + self.assertAllEqual(downscaled_masks_shape_out, expected_masks_shape) + + @parameterized.parameters( + {'test_masks': False}, + {'test_masks': True} + ) + def testRandomDownscaleToTargetPixelsWithCache(self, test_masks): + preprocessing_options = [(preprocessor.random_downscale_to_target_pixels, { + 'min_target_pixels': 100, + 'max_target_pixels': 999 + })] + self._testPreprocessorCache(preprocessing_options, test_masks=test_masks) + + def testRandomDownscaleToTargetPixelsWithRandomCoefOne(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_downscale_to_target_pixels, + { + 'random_coef': 1.0, + 'min_target_pixels': 10, + 'max_target_pixels': 20, + })] + images = tf.random_uniform([1, 25, 100, 3]) + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + downscaled_images = processed_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + downscaled_images_shape = tf.shape(downscaled_images) + return [images, downscaled_images, images_shape, downscaled_images_shape] + (images_out, downscaled_images_out, images_shape_out, + downscaled_images_shape_out) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape_out, downscaled_images_shape_out) + self.assertAllEqual(images_out, downscaled_images_out) + + def testRandomDownscaleToTargetPixelsIgnoresSmallImages(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_downscale_to_target_pixels, + { + 'min_target_pixels': 1000, + 'max_target_pixels': 1001 + })] + images = tf.random_uniform([1, 10, 10, 3]) + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + downscaled_images = processed_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + downscaled_images_shape = tf.shape(downscaled_images) + return [images, downscaled_images, images_shape, downscaled_images_shape] + (images_out, downscaled_images_out, images_shape_out, + downscaled_images_shape_out) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape_out, downscaled_images_shape_out) + self.assertAllEqual(images_out, downscaled_images_out) + + def testRandomPatchGaussianShape(self): + preprocessing_options = [(preprocessor.random_patch_gaussian, { + 'min_patch_size': 1, + 'max_patch_size': 200, + 'min_gaussian_stddev': 0.0, + 'max_gaussian_stddev': 2.0 + })] + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + patched_images = processed_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + patched_images_shape = tf.shape(patched_images) + self.assertAllEqual(images_shape, patched_images_shape) + + def testRandomPatchGaussianClippedToLowerBound(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_patch_gaussian, { + 'min_patch_size': 20, + 'max_patch_size': 40, + 'min_gaussian_stddev': 50, + 'max_gaussian_stddev': 100 + })] + images = tf.zeros([1, 5, 4, 3]) + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + patched_images = processed_tensor_dict[fields.InputDataFields.image] + return patched_images + patched_images = self.execute_cpu(graph_fn, []) + self.assertAllGreaterEqual(patched_images, 0.0) + + def testRandomPatchGaussianClippedToUpperBound(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_patch_gaussian, { + 'min_patch_size': 20, + 'max_patch_size': 40, + 'min_gaussian_stddev': 50, + 'max_gaussian_stddev': 100 + })] + images = tf.constant(255.0, shape=[1, 5, 4, 3]) + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + patched_images = processed_tensor_dict[fields.InputDataFields.image] + return patched_images + patched_images = self.execute_cpu(graph_fn, []) + self.assertAllLessEqual(patched_images, 255.0) + + def testRandomPatchGaussianWithCache(self): + preprocessing_options = [(preprocessor.random_patch_gaussian, { + 'min_patch_size': 1, + 'max_patch_size': 200, + 'min_gaussian_stddev': 0.0, + 'max_gaussian_stddev': 2.0 + })] + self._testPreprocessorCache(preprocessing_options) + + def testRandomPatchGaussianWithRandomCoefOne(self): + def graph_fn(): + preprocessing_options = [(preprocessor.random_patch_gaussian, { + 'random_coef': 1.0 + })] + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + processed_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + patched_images = processed_tensor_dict[fields.InputDataFields.image] + images_shape = tf.shape(images) + patched_images_shape = tf.shape(patched_images) + return patched_images_shape, patched_images, images_shape, images + (patched_images_shape, patched_images, images_shape, + images) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape, patched_images_shape) + self.assertAllEqual(images, patched_images) + + @unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only test.') + def testAutoAugmentImage(self): + def graph_fn(): + preprocessing_options = [] + preprocessing_options.append((preprocessor.autoaugment_image, { + 'policy_name': 'v1' + })) + images = self.createTestImages() + boxes = self.createTestBoxes() + tensor_dict = {fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes} + autoaugment_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options) + augmented_images = autoaugment_tensor_dict[fields.InputDataFields.image] + augmented_boxes = autoaugment_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + images_shape = tf.shape(images) + boxes_shape = tf.shape(boxes) + augmented_images_shape = tf.shape(augmented_images) + augmented_boxes_shape = tf.shape(augmented_boxes) + return [images_shape, boxes_shape, augmented_images_shape, + augmented_boxes_shape] + (images_shape_, boxes_shape_, augmented_images_shape_, + augmented_boxes_shape_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(images_shape_, augmented_images_shape_) + self.assertAllEqual(boxes_shape_, augmented_boxes_shape_) + + def testRandomResizeMethodWithCache(self): + preprocess_options = [] + preprocess_options.append((preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })) + preprocess_options.append((preprocessor.random_resize_method, { + 'target_size': (75, 150) + })) + self._testPreprocessorCache(preprocess_options, + test_boxes=True, + test_masks=True, + test_keypoints=True) + + def testRandomResizeMethod(self): + def graph_fn(): + preprocessing_options = [] + preprocessing_options.append((preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + })) + preprocessing_options.append((preprocessor.random_resize_method, { + 'target_size': (75, 150) + })) + images = self.createTestImages() + tensor_dict = {fields.InputDataFields.image: images} + resized_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + resized_images = resized_tensor_dict[fields.InputDataFields.image] + resized_images_shape = tf.shape(resized_images) + expected_images_shape = tf.constant([1, 75, 150, 3], dtype=tf.int32) + return [expected_images_shape, resized_images_shape] + (expected_images_shape_, resized_images_shape_) = self.execute_cpu(graph_fn, + []) + self.assertAllEqual(expected_images_shape_, + resized_images_shape_) + + def testResizeImageWithMasks(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 60, 40], [10, 15, 30]] + height = 50 + width = 100 + expected_image_shape_list = [[50, 100, 3], [50, 100, 3]] + expected_masks_shape_list = [[15, 50, 100], [10, 50, 100]] + def graph_fn(in_image_shape, in_masks_shape): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_image( + in_image, in_masks, new_height=height, new_width=width) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return out_image_shape, out_masks_shape + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + (out_image_shape, + out_masks_shape) = self.execute_cpu(graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeImageWithMasksTensorInputHeightAndWidth(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 60, 40], [10, 15, 30]] + expected_image_shape_list = [[50, 100, 3], [50, 100, 3]] + expected_masks_shape_list = [[15, 50, 100], [10, 50, 100]] + def graph_fn(in_image_shape, in_masks_shape): + height = tf.constant(50, dtype=tf.int32) + width = tf.constant(100, dtype=tf.int32) + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_image( + in_image, in_masks, new_height=height, new_width=width) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return out_image_shape, out_masks_shape + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + (out_image_shape, + out_masks_shape) = self.execute_cpu(graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeImageWithNoInstanceMask(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[0, 60, 40], [0, 15, 30]] + height = 50 + width = 100 + expected_image_shape_list = [[50, 100, 3], [50, 100, 3]] + expected_masks_shape_list = [[0, 50, 100], [0, 50, 100]] + def graph_fn(in_image_shape, in_masks_shape): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_image( + in_image, in_masks, new_height=height, new_width=width) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return out_image_shape, out_masks_shape + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + (out_image_shape, + out_masks_shape) = self.execute_cpu(graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToRangePreservesStaticSpatialShape(self): + """Tests image resizing, checking output sizes.""" + in_shape_list = [[60, 40, 3], [15, 30, 3], [15, 50, 3]] + min_dim = 50 + max_dim = 100 + expected_shape_list = [[75, 50, 3], [50, 100, 3], [30, 100, 3]] + + for in_shape, expected_shape in zip(in_shape_list, expected_shape_list): + in_image = tf.random_uniform(in_shape) + out_image, _ = preprocessor.resize_to_range( + in_image, min_dimension=min_dim, max_dimension=max_dim) + self.assertAllEqual(out_image.get_shape().as_list(), expected_shape) + + def testResizeToRangeWithDynamicSpatialShape(self): + """Tests image resizing, checking output sizes.""" + in_shape_list = [[60, 40, 3], [15, 30, 3], [15, 50, 3]] + min_dim = 50 + max_dim = 100 + expected_shape_list = [[75, 50, 3], [50, 100, 3], [30, 100, 3]] + def graph_fn(in_image_shape): + in_image = tf.random_uniform(in_image_shape) + out_image, _ = preprocessor.resize_to_range( + in_image, min_dimension=min_dim, max_dimension=max_dim) + out_image_shape = tf.shape(out_image) + return out_image_shape + for in_shape, expected_shape in zip(in_shape_list, expected_shape_list): + out_image_shape = self.execute_cpu(graph_fn, [np.array(in_shape, + np.int32)]) + self.assertAllEqual(out_image_shape, expected_shape) + + def testResizeToRangeWithPadToMaxDimensionReturnsCorrectShapes(self): + in_shape_list = [[60, 40, 3], [15, 30, 3], [15, 50, 3]] + min_dim = 50 + max_dim = 100 + expected_shape_list = [[100, 100, 3], [100, 100, 3], [100, 100, 3]] + def graph_fn(in_image): + out_image, _ = preprocessor.resize_to_range( + in_image, + min_dimension=min_dim, + max_dimension=max_dim, + pad_to_max_dimension=True) + return tf.shape(out_image) + for in_shape, expected_shape in zip(in_shape_list, expected_shape_list): + out_image_shape = self.execute_cpu( + graph_fn, [np.random.rand(*in_shape).astype('f')]) + self.assertAllEqual(out_image_shape, expected_shape) + + def testResizeToRangeWithPadToMaxDimensionReturnsCorrectTensor(self): + in_image_np = np.array([[[0, 1, 2]]], np.float32) + ex_image_np = np.array( + [[[0, 1, 2], [123.68, 116.779, 103.939]], + [[123.68, 116.779, 103.939], [123.68, 116.779, 103.939]]], np.float32) + min_dim = 1 + max_dim = 2 + def graph_fn(in_image): + out_image, _ = preprocessor.resize_to_range( + in_image, + min_dimension=min_dim, + max_dimension=max_dim, + pad_to_max_dimension=True, + per_channel_pad_value=(123.68, 116.779, 103.939)) + return out_image + out_image_np = self.execute_cpu(graph_fn, [in_image_np]) + self.assertAllClose(ex_image_np, out_image_np) + + def testResizeToRangeWithMasksPreservesStaticSpatialShape(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 60, 40], [10, 15, 30]] + min_dim = 50 + max_dim = 100 + expected_image_shape_list = [[75, 50, 3], [50, 100, 3]] + expected_masks_shape_list = [[15, 75, 50], [10, 50, 100]] + + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_to_range( + in_image, in_masks, min_dimension=min_dim, max_dimension=max_dim) + self.assertAllEqual(out_masks.get_shape().as_list(), expected_mask_shape) + self.assertAllEqual(out_image.get_shape().as_list(), expected_image_shape) + + def testResizeToRangeWithMasksAndPadToMaxDimension(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 60, 40], [10, 15, 30]] + min_dim = 50 + max_dim = 100 + expected_image_shape_list = [[100, 100, 3], [100, 100, 3]] + expected_masks_shape_list = [[15, 100, 100], [10, 100, 100]] + def graph_fn(in_image, in_masks): + out_image, out_masks, _ = preprocessor.resize_to_range( + in_image, in_masks, min_dimension=min_dim, + max_dimension=max_dim, pad_to_max_dimension=True) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.random.rand(*in_image_shape).astype('f'), + np.random.rand(*in_masks_shape).astype('f'), + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToRangeWithMasksAndDynamicSpatialShape(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 60, 40], [10, 15, 30]] + min_dim = 50 + max_dim = 100 + expected_image_shape_list = [[75, 50, 3], [50, 100, 3]] + expected_masks_shape_list = [[15, 75, 50], [10, 50, 100]] + def graph_fn(in_image, in_masks): + out_image, out_masks, _ = preprocessor.resize_to_range( + in_image, in_masks, min_dimension=min_dim, max_dimension=max_dim) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.random.rand(*in_image_shape).astype('f'), + np.random.rand(*in_masks_shape).astype('f'), + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToRangeWithInstanceMasksTensorOfSizeZero(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[0, 60, 40], [0, 15, 30]] + min_dim = 50 + max_dim = 100 + expected_image_shape_list = [[75, 50, 3], [50, 100, 3]] + expected_masks_shape_list = [[0, 75, 50], [0, 50, 100]] + def graph_fn(in_image, in_masks): + out_image, out_masks, _ = preprocessor.resize_to_range( + in_image, in_masks, min_dimension=min_dim, max_dimension=max_dim) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.random.rand(*in_image_shape).astype('f'), + np.random.rand(*in_masks_shape).astype('f'), + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToRange4DImageTensor(self): + image = tf.random_uniform([1, 200, 300, 3]) + with self.assertRaises(ValueError): + preprocessor.resize_to_range(image, 500, 600) + + def testResizeToRangeSameMinMax(self): + """Tests image resizing, checking output sizes.""" + in_shape_list = [[312, 312, 3], [299, 299, 3]] + min_dim = 320 + max_dim = 320 + expected_shape_list = [[320, 320, 3], [320, 320, 3]] + def graph_fn(in_shape): + in_image = tf.random_uniform(in_shape) + out_image, _ = preprocessor.resize_to_range( + in_image, min_dimension=min_dim, max_dimension=max_dim) + out_image_shape = tf.shape(out_image) + return out_image_shape + for in_shape, expected_shape in zip(in_shape_list, expected_shape_list): + out_image_shape = self.execute_cpu(graph_fn, [np.array(in_shape, + np.int32)]) + self.assertAllEqual(out_image_shape, expected_shape) + + def testResizeToMaxDimensionTensorShapes(self): + """Tests both cases where image should and shouldn't be resized.""" + in_image_shape_list = [[100, 50, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 100, 50], [10, 15, 30]] + max_dim = 50 + expected_image_shape_list = [[50, 25, 3], [15, 30, 3]] + expected_masks_shape_list = [[15, 50, 25], [10, 15, 30]] + def graph_fn(in_image_shape, in_masks_shape): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_to_max_dimension( + in_image, in_masks, max_dimension=max_dim) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToMaxDimensionWithInstanceMasksTensorOfSizeZero(self): + """Tests both cases where image should and shouldn't be resized.""" + in_image_shape_list = [[100, 50, 3], [15, 30, 3]] + in_masks_shape_list = [[0, 100, 50], [0, 15, 30]] + max_dim = 50 + expected_image_shape_list = [[50, 25, 3], [15, 30, 3]] + expected_masks_shape_list = [[0, 50, 25], [0, 15, 30]] + + def graph_fn(in_image_shape, in_masks_shape): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_to_max_dimension( + in_image, in_masks, max_dimension=max_dim) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToMaxDimensionRaisesErrorOn4DImage(self): + image = tf.random_uniform([1, 200, 300, 3]) + with self.assertRaises(ValueError): + preprocessor.resize_to_max_dimension(image, 500) + + def testResizeToMinDimensionTensorShapes(self): + in_image_shape_list = [[60, 55, 3], [15, 30, 3]] + in_masks_shape_list = [[15, 60, 55], [10, 15, 30]] + min_dim = 50 + expected_image_shape_list = [[60, 55, 3], [50, 100, 3]] + expected_masks_shape_list = [[15, 60, 55], [10, 50, 100]] + def graph_fn(in_image_shape, in_masks_shape): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_to_min_dimension( + in_image, in_masks, min_dimension=min_dim) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToMinDimensionWithInstanceMasksTensorOfSizeZero(self): + """Tests image resizing, checking output sizes.""" + in_image_shape_list = [[60, 40, 3], [15, 30, 3]] + in_masks_shape_list = [[0, 60, 40], [0, 15, 30]] + min_dim = 50 + expected_image_shape_list = [[75, 50, 3], [50, 100, 3]] + expected_masks_shape_list = [[0, 75, 50], [0, 50, 100]] + def graph_fn(in_image_shape, in_masks_shape): + in_image = tf.random_uniform(in_image_shape) + in_masks = tf.random_uniform(in_masks_shape) + out_image, out_masks, _ = preprocessor.resize_to_min_dimension( + in_image, in_masks, min_dimension=min_dim) + out_image_shape = tf.shape(out_image) + out_masks_shape = tf.shape(out_masks) + return [out_image_shape, out_masks_shape] + for (in_image_shape, expected_image_shape, in_masks_shape, + expected_mask_shape) in zip(in_image_shape_list, + expected_image_shape_list, + in_masks_shape_list, + expected_masks_shape_list): + out_image_shape, out_masks_shape = self.execute_cpu( + graph_fn, [ + np.array(in_image_shape, np.int32), + np.array(in_masks_shape, np.int32) + ]) + self.assertAllEqual(out_image_shape, expected_image_shape) + self.assertAllEqual(out_masks_shape, expected_mask_shape) + + def testResizeToMinDimensionRaisesErrorOn4DImage(self): + image = tf.random_uniform([1, 200, 300, 3]) + with self.assertRaises(ValueError): + preprocessor.resize_to_min_dimension(image, 500) + + def testResizePadToMultipleNoMasks(self): + """Tests resizing when padding to multiple without masks.""" + def graph_fn(): + image = tf.ones((200, 100, 3), dtype=tf.float32) + out_image, out_shape = preprocessor.resize_pad_to_multiple( + image, multiple=32) + return out_image, out_shape + + out_image, out_shape = self.execute_cpu(graph_fn, []) + self.assertAllClose(out_image.sum(), 200 * 100 * 3) + self.assertAllEqual(out_shape, (200, 100, 3)) + self.assertAllEqual(out_image.shape, (224, 128, 3)) + + def testResizePadToMultipleWithMasks(self): + """Tests resizing when padding to multiple with masks.""" + def graph_fn(): + image = tf.ones((200, 100, 3), dtype=tf.float32) + masks = tf.ones((10, 200, 100), dtype=tf.float32) + + _, out_masks, out_shape = preprocessor.resize_pad_to_multiple( + image, multiple=32, masks=masks) + return [out_masks, out_shape] + + out_masks, out_shape = self.execute_cpu(graph_fn, []) + self.assertAllClose(out_masks.sum(), 200 * 100 * 10) + self.assertAllEqual(out_shape, (200, 100, 3)) + self.assertAllEqual(out_masks.shape, (10, 224, 128)) + + def testResizePadToMultipleEmptyMasks(self): + """Tests resizing when padding to multiple with an empty mask.""" + def graph_fn(): + image = tf.ones((200, 100, 3), dtype=tf.float32) + masks = tf.ones((0, 200, 100), dtype=tf.float32) + _, out_masks, out_shape = preprocessor.resize_pad_to_multiple( + image, multiple=32, masks=masks) + return [out_masks, out_shape] + out_masks, out_shape = self.execute_cpu(graph_fn, []) + self.assertAllEqual(out_shape, (200, 100, 3)) + self.assertAllEqual(out_masks.shape, (0, 224, 128)) + + def testScaleBoxesToPixelCoordinates(self): + """Tests box scaling, checking scaled values.""" + def graph_fn(): + in_shape = [60, 40, 3] + in_boxes = [[0.1, 0.2, 0.4, 0.6], + [0.5, 0.3, 0.9, 0.7]] + in_image = tf.random_uniform(in_shape) + in_boxes = tf.constant(in_boxes) + _, out_boxes = preprocessor.scale_boxes_to_pixel_coordinates( + in_image, boxes=in_boxes) + return out_boxes + expected_boxes = [[6., 8., 24., 24.], + [30., 12., 54., 28.]] + out_boxes = self.execute_cpu(graph_fn, []) + self.assertAllClose(out_boxes, expected_boxes) + + def testScaleBoxesToPixelCoordinatesWithKeypoints(self): + """Tests box and keypoint scaling, checking scaled values.""" + def graph_fn(): + in_shape = [60, 40, 3] + in_boxes = self.createTestBoxes() + in_keypoints, _ = self.createTestKeypoints() + in_image = tf.random_uniform(in_shape) + (_, out_boxes, + out_keypoints) = preprocessor.scale_boxes_to_pixel_coordinates( + in_image, boxes=in_boxes, keypoints=in_keypoints) + return out_boxes, out_keypoints + expected_boxes = [[0., 10., 45., 40.], + [15., 20., 45., 40.]] + expected_keypoints = [ + [[6., 4.], [12., 8.], [18., 12.]], + [[24., 16.], [30., 20.], [36., 24.]], + ] + out_boxes_, out_keypoints_ = self.execute_cpu(graph_fn, []) + self.assertAllClose(out_boxes_, expected_boxes) + self.assertAllClose(out_keypoints_, expected_keypoints) + + def testSubtractChannelMean(self): + """Tests whether channel means have been subtracted.""" + def graph_fn(): + image = tf.zeros((240, 320, 3)) + means = [1, 2, 3] + actual = preprocessor.subtract_channel_mean(image, means=means) + return actual + actual = self.execute_cpu(graph_fn, []) + self.assertTrue((actual[:, :, 0], -1)) + self.assertTrue((actual[:, :, 1], -2)) + self.assertTrue((actual[:, :, 2], -3)) + + def testOneHotEncoding(self): + """Tests one hot encoding of multiclass labels.""" + def graph_fn(): + labels = tf.constant([1, 4, 2], dtype=tf.int32) + one_hot = preprocessor.one_hot_encoding(labels, num_classes=5) + return one_hot + one_hot = self.execute_cpu(graph_fn, []) + self.assertAllEqual([0, 1, 1, 0, 1], one_hot) + + def testRandomSelfConcatImageVertically(self): + + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + confidences = weights + scores = self.createTestMultiClassScores() + + tensor_dict = { + fields.InputDataFields.image: tf.cast(images, dtype=tf.float32), + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights, + fields.InputDataFields.groundtruth_confidences: confidences, + fields.InputDataFields.multiclass_scores: scores, + } + + preprocessing_options = [(preprocessor.random_self_concat_image, { + 'concat_vertical_probability': 1.0, + 'concat_horizontal_probability': 0.0, + })] + func_arg_map = preprocessor.get_default_func_arg_map( + True, True, True) + output_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=func_arg_map) + + original_shape = tf.shape(images)[1:3] + final_shape = tf.shape(output_tensor_dict[fields.InputDataFields.image])[ + 1:3] + return [ + original_shape, + boxes, + labels, + confidences, + scores, + final_shape, + output_tensor_dict[fields.InputDataFields.groundtruth_boxes], + output_tensor_dict[fields.InputDataFields.groundtruth_classes], + output_tensor_dict[fields.InputDataFields.groundtruth_confidences], + output_tensor_dict[fields.InputDataFields.multiclass_scores], + ] + (original_shape, boxes, labels, confidences, scores, final_shape, new_boxes, + new_labels, new_confidences, new_scores) = self.execute(graph_fn, []) + self.assertAllEqual(final_shape, original_shape * np.array([2, 1])) + self.assertAllEqual(2 * boxes.size, new_boxes.size) + self.assertAllEqual(2 * labels.size, new_labels.size) + self.assertAllEqual(2 * confidences.size, new_confidences.size) + self.assertAllEqual(2 * scores.size, new_scores.size) + + def testRandomSelfConcatImageHorizontally(self): + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + confidences = weights + scores = self.createTestMultiClassScores() + + tensor_dict = { + fields.InputDataFields.image: tf.cast(images, dtype=tf.float32), + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights, + fields.InputDataFields.groundtruth_confidences: confidences, + fields.InputDataFields.multiclass_scores: scores, + } + + preprocessing_options = [(preprocessor.random_self_concat_image, { + 'concat_vertical_probability': 0.0, + 'concat_horizontal_probability': 1.0, + })] + func_arg_map = preprocessor.get_default_func_arg_map( + True, True, True) + output_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=func_arg_map) + + original_shape = tf.shape(images)[1:3] + final_shape = tf.shape(output_tensor_dict[fields.InputDataFields.image])[ + 1:3] + return [ + original_shape, + boxes, + labels, + confidences, + scores, + final_shape, + output_tensor_dict[fields.InputDataFields.groundtruth_boxes], + output_tensor_dict[fields.InputDataFields.groundtruth_classes], + output_tensor_dict[fields.InputDataFields.groundtruth_confidences], + output_tensor_dict[fields.InputDataFields.multiclass_scores], + ] + (original_shape, boxes, labels, confidences, scores, final_shape, new_boxes, + new_labels, new_confidences, new_scores) = self.execute(graph_fn, []) + self.assertAllEqual(final_shape, original_shape * np.array([1, 2])) + self.assertAllEqual(2 * boxes.size, new_boxes.size) + self.assertAllEqual(2 * labels.size, new_labels.size) + self.assertAllEqual(2 * confidences.size, new_confidences.size) + self.assertAllEqual(2 * scores.size, new_scores.size) + + def testSSDRandomCropWithCache(self): + preprocess_options = [ + (preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + }), + (preprocessor.ssd_random_crop, {})] + self._testPreprocessorCache(preprocess_options, + test_boxes=True, + test_masks=False, + test_keypoints=False) + + def testSSDRandomCrop(self): + def graph_fn(): + preprocessing_options = [ + (preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + }), + (preprocessor.ssd_random_crop, {})] + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights, + } + distorted_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + distorted_images = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + return [boxes_rank, distorted_boxes_rank, images_rank, + distorted_images_rank] + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testSSDRandomCropWithMultiClassScores(self): + def graph_fn(): + preprocessing_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + }), (preprocessor.ssd_random_crop, {})] + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + multiclass_scores = self.createTestMultiClassScores() + + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.multiclass_scores: multiclass_scores, + fields.InputDataFields.groundtruth_weights: weights, + } + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_multiclass_scores=True) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_images = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + distorted_multiclass_scores = distorted_tensor_dict[ + fields.InputDataFields.multiclass_scores] + + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + multiclass_scores_rank = tf.rank(multiclass_scores) + distorted_multiclass_scores_rank = tf.rank(distorted_multiclass_scores) + return [ + boxes_rank, distorted_boxes, distorted_boxes_rank, images_rank, + distorted_images_rank, multiclass_scores_rank, + distorted_multiclass_scores, distorted_multiclass_scores_rank + ] + + (boxes_rank_, distorted_boxes_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_, multiclass_scores_rank_, + distorted_multiclass_scores_, + distorted_multiclass_scores_rank_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + self.assertAllEqual(multiclass_scores_rank_, + distorted_multiclass_scores_rank_) + self.assertAllEqual(distorted_boxes_.shape[0], + distorted_multiclass_scores_.shape[0]) + + def testSSDRandomCropPad(self): + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + preprocessing_options = [ + (preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + }), + (preprocessor.ssd_random_crop_pad, {})] + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights, + } + distorted_tensor_dict = preprocessor.preprocess(tensor_dict, + preprocessing_options) + distorted_images = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + return [ + boxes_rank, distorted_boxes_rank, images_rank, distorted_images_rank + ] + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testSSDRandomCropFixedAspectRatioWithCache(self): + preprocess_options = [ + (preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + }), + (preprocessor.ssd_random_crop_fixed_aspect_ratio, {})] + self._testPreprocessorCache(preprocess_options, + test_boxes=True, + test_masks=False, + test_keypoints=False) + + def _testSSDRandomCropFixedAspectRatio(self, + include_multiclass_scores, + include_instance_masks, + include_keypoints): + def graph_fn(): + images = self.createTestImages() + boxes = self.createTestBoxes() + labels = self.createTestLabels() + weights = self.createTestGroundtruthWeights() + preprocessing_options = [(preprocessor.normalize_image, { + 'original_minval': 0, + 'original_maxval': 255, + 'target_minval': 0, + 'target_maxval': 1 + }), (preprocessor.ssd_random_crop_fixed_aspect_ratio, {})] + tensor_dict = { + fields.InputDataFields.image: images, + fields.InputDataFields.groundtruth_boxes: boxes, + fields.InputDataFields.groundtruth_classes: labels, + fields.InputDataFields.groundtruth_weights: weights + } + if include_multiclass_scores: + multiclass_scores = self.createTestMultiClassScores() + tensor_dict[fields.InputDataFields.multiclass_scores] = ( + multiclass_scores) + if include_instance_masks: + masks = self.createTestMasks() + tensor_dict[fields.InputDataFields.groundtruth_instance_masks] = masks + if include_keypoints: + keypoints, _ = self.createTestKeypoints() + tensor_dict[fields.InputDataFields.groundtruth_keypoints] = keypoints + + preprocessor_arg_map = preprocessor.get_default_func_arg_map( + include_multiclass_scores=include_multiclass_scores, + include_instance_masks=include_instance_masks, + include_keypoints=include_keypoints) + distorted_tensor_dict = preprocessor.preprocess( + tensor_dict, preprocessing_options, func_arg_map=preprocessor_arg_map) + distorted_images = distorted_tensor_dict[fields.InputDataFields.image] + distorted_boxes = distorted_tensor_dict[ + fields.InputDataFields.groundtruth_boxes] + images_rank = tf.rank(images) + distorted_images_rank = tf.rank(distorted_images) + boxes_rank = tf.rank(boxes) + distorted_boxes_rank = tf.rank(distorted_boxes) + return [boxes_rank, distorted_boxes_rank, images_rank, + distorted_images_rank] + + (boxes_rank_, distorted_boxes_rank_, images_rank_, + distorted_images_rank_) = self.execute_cpu(graph_fn, []) + self.assertAllEqual(boxes_rank_, distorted_boxes_rank_) + self.assertAllEqual(images_rank_, distorted_images_rank_) + + def testSSDRandomCropFixedAspectRatio(self): + self._testSSDRandomCropFixedAspectRatio(include_multiclass_scores=False, + include_instance_masks=False, + include_keypoints=False) + + def testSSDRandomCropFixedAspectRatioWithMultiClassScores(self): + self._testSSDRandomCropFixedAspectRatio(include_multiclass_scores=True, + include_instance_masks=False, + include_keypoints=False) + + def testSSDRandomCropFixedAspectRatioWithMasksAndKeypoints(self): + self._testSSDRandomCropFixedAspectRatio(include_multiclass_scores=False, + include_instance_masks=True, + include_keypoints=True) + + def testSSDRandomCropFixedAspectRatioWithLabelScoresMasksAndKeypoints(self): + self._testSSDRandomCropFixedAspectRatio(include_multiclass_scores=False, + include_instance_masks=True, + include_keypoints=True) + + def testConvertClassLogitsToSoftmax(self): + def graph_fn(): + multiclass_scores = tf.constant( + [[1.0, 0.0], [0.5, 0.5], [1000, 1]], dtype=tf.float32) + temperature = 2.0 + + converted_multiclass_scores = ( + preprocessor.convert_class_logits_to_softmax( + multiclass_scores=multiclass_scores, temperature=temperature)) + return converted_multiclass_scores + converted_multiclass_scores_ = self.execute_cpu(graph_fn, []) + expected_converted_multiclass_scores = [[0.62245935, 0.37754068], + [0.5, 0.5], + [1, 0]] + self.assertAllClose(converted_multiclass_scores_, + expected_converted_multiclass_scores) + + @parameterized.named_parameters( + ('scale_1', 1.0), + ('scale_1.5', 1.5), + ('scale_0.5', 0.5) + ) + def test_square_crop_by_scale(self, scale): + def graph_fn(): + image = np.random.randn(256, 256, 1) + + masks = tf.constant(image[:, :, 0].reshape(1, 256, 256)) + image = tf.constant(image) + keypoints = tf.constant([[[0.25, 0.25], [0.75, 0.75]]]) + + boxes = tf.constant([[0.25, .25, .75, .75]]) + labels = tf.constant([[1]]) + label_confidences = tf.constant([0.75]) + label_weights = tf.constant([[1.]]) + + (new_image, new_boxes, _, _, new_confidences, new_masks, + new_keypoints) = preprocessor.random_square_crop_by_scale( + image, + boxes, + labels, + label_weights, + label_confidences, + masks=masks, + keypoints=keypoints, + max_border=256, + scale_min=scale, + scale_max=scale) + return new_image, new_boxes, new_confidences, new_masks, new_keypoints + image, boxes, confidences, masks, keypoints = self.execute_cpu(graph_fn, []) + ymin, xmin, ymax, xmax = boxes[0] + self.assertAlmostEqual(ymax - ymin, 0.5 / scale) + self.assertAlmostEqual(xmax - xmin, 0.5 / scale) + + k1 = keypoints[0, 0] + k2 = keypoints[0, 1] + self.assertAlmostEqual(k2[0] - k1[0], 0.5 / scale) + self.assertAlmostEqual(k2[1] - k1[1], 0.5 / scale) + + size = max(image.shape) + self.assertAlmostEqual(scale * 256.0, size) + + self.assertAllClose(image[:, :, 0], masks[0, :, :]) + self.assertAllClose(confidences, [0.75]) + + @parameterized.named_parameters(('scale_0_1', 0.1), ('scale_1_0', 1.0), + ('scale_2_0', 2.0)) + def test_random_scale_crop_and_pad_to_square(self, scale): + + def graph_fn(): + image = np.random.randn(512, 256, 1) + box_centers = [0.25, 0.5, 0.75] + box_size = 0.1 + box_corners = [] + box_labels = [] + box_label_weights = [] + keypoints = [] + masks = [] + for center_y in box_centers: + for center_x in box_centers: + box_corners.append( + [center_y - box_size / 2.0, center_x - box_size / 2.0, + center_y + box_size / 2.0, center_x + box_size / 2.0]) + box_labels.append([1]) + box_label_weights.append([1.]) + keypoints.append( + [[center_y - box_size / 2.0, center_x - box_size / 2.0], + [center_y + box_size / 2.0, center_x + box_size / 2.0]]) + masks.append(image[:, :, 0].reshape(512, 256)) + + image = tf.constant(image) + boxes = tf.constant(box_corners) + labels = tf.constant(box_labels) + label_weights = tf.constant(box_label_weights) + keypoints = tf.constant(keypoints) + masks = tf.constant(np.stack(masks)) + + (new_image, new_boxes, _, _, new_masks, + new_keypoints) = preprocessor.random_scale_crop_and_pad_to_square( + image, + boxes, + labels, + label_weights, + masks=masks, + keypoints=keypoints, + scale_min=scale, + scale_max=scale, + output_size=512) + return new_image, new_boxes, new_masks, new_keypoints + + image, boxes, masks, keypoints = self.execute_cpu(graph_fn, []) + + # Since random_scale_crop_and_pad_to_square may prune and clip boxes, + # we only need to find one of the boxes that was not clipped and check + # that it matches the expected dimensions. Note, assertAlmostEqual(a, b) + # is equivalent to round(a-b, 7) == 0. + any_box_has_correct_size = False + effective_scale_y = int(scale * 512) / 512.0 + effective_scale_x = int(scale * 256) / 512.0 + expected_size_y = 0.1 * effective_scale_y + expected_size_x = 0.1 * effective_scale_x + for box in boxes: + ymin, xmin, ymax, xmax = box + any_box_has_correct_size |= ( + (round(ymin, 7) != 0.0) and (round(xmin, 7) != 0.0) and + (round(ymax, 7) != 1.0) and (round(xmax, 7) != 1.0) and + (round((ymax - ymin) - expected_size_y, 7) == 0.0) and + (round((xmax - xmin) - expected_size_x, 7) == 0.0)) + self.assertTrue(any_box_has_correct_size) + + # Similar to the approach above where we check for at least one box with the + # expected dimensions, we check for at least one pair of keypoints whose + # distance matches the expected dimensions. + any_keypoint_pair_has_correct_dist = False + for keypoint_pair in keypoints: + ymin, xmin = keypoint_pair[0] + ymax, xmax = keypoint_pair[1] + any_keypoint_pair_has_correct_dist |= ( + (round(ymin, 7) != 0.0) and (round(xmin, 7) != 0.0) and + (round(ymax, 7) != 1.0) and (round(xmax, 7) != 1.0) and + (round((ymax - ymin) - expected_size_y, 7) == 0.0) and + (round((xmax - xmin) - expected_size_x, 7) == 0.0)) + self.assertTrue(any_keypoint_pair_has_correct_dist) + + self.assertAlmostEqual(512.0, image.shape[0]) + self.assertAlmostEqual(512.0, image.shape[1]) + + self.assertAllClose(image[:, :, 0], + masks[0, :, :]) + + def test_random_scale_crop_and_pad_to_square_handles_confidences(self): + + def graph_fn(): + image = tf.zeros([10, 10, 1]) + boxes = tf.constant([[0, 0, 0.5, 0.5], [0.5, 0.5, 0.75, 0.75]]) + label_weights = tf.constant([1.0, 1.0]) + box_labels = tf.constant([0, 1]) + box_confidences = tf.constant([-1.0, 1.0]) + + (_, new_boxes, _, _, + new_confidences) = preprocessor.random_scale_crop_and_pad_to_square( + image, + boxes, + box_labels, + label_weights, + label_confidences=box_confidences, + scale_min=0.8, + scale_max=0.9, + output_size=10) + return new_boxes, new_confidences + + boxes, confidences = self.execute_cpu(graph_fn, []) + + self.assertLen(boxes, 2) + self.assertAllEqual(confidences, [-1.0, 1.0]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/region_similarity_calculator.py b/workspace/virtuallab/object_detection/core/region_similarity_calculator.py new file mode 100644 index 0000000..fcaba76 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/region_similarity_calculator.py @@ -0,0 +1,193 @@ +# Lint as: python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Region Similarity Calculators for BoxLists. + +Region Similarity Calculators compare a pairwise measure of similarity +between the boxes in two BoxLists. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from abc import ABCMeta +from abc import abstractmethod + +import six +import tensorflow.compat.v1 as tf + +from object_detection.core import box_list_ops +from object_detection.core import standard_fields as fields + + +class RegionSimilarityCalculator(six.with_metaclass(ABCMeta, object)): + """Abstract base class for region similarity calculator.""" + + def compare(self, boxlist1, boxlist2, scope=None): + """Computes matrix of pairwise similarity between BoxLists. + + This op (to be overridden) computes a measure of pairwise similarity between + the boxes in the given BoxLists. Higher values indicate more similarity. + + Note that this method simply measures similarity and does not explicitly + perform a matching. + + Args: + boxlist1: BoxList holding N boxes. + boxlist2: BoxList holding M boxes. + scope: Op scope name. Defaults to 'Compare' if None. + + Returns: + a (float32) tensor of shape [N, M] with pairwise similarity score. + """ + with tf.name_scope(scope, 'Compare', [boxlist1, boxlist2]) as scope: + return self._compare(boxlist1, boxlist2) + + @abstractmethod + def _compare(self, boxlist1, boxlist2): + pass + + +class IouSimilarity(RegionSimilarityCalculator): + """Class to compute similarity based on Intersection over Union (IOU) metric. + + This class computes pairwise similarity between two BoxLists based on IOU. + """ + + def _compare(self, boxlist1, boxlist2): + """Compute pairwise IOU similarity between the two BoxLists. + + Args: + boxlist1: BoxList holding N boxes. + boxlist2: BoxList holding M boxes. + + Returns: + A tensor with shape [N, M] representing pairwise iou scores. + """ + return box_list_ops.iou(boxlist1, boxlist2) + + +class DETRSimilarity(RegionSimilarityCalculator): + """Class to compute similarity for the Detection Transformer model. + + This class computes pairwise DETR similarity between two BoxLists using a + weighted combination of GIOU, classification scores, and the L1 loss. + """ + + def __init__(self, l1_weight=5, giou_weight=2): + super().__init__() + self.l1_weight = l1_weight + self.giou_weight = giou_weight + + def _compare(self, boxlist1, boxlist2): + """Compute pairwise DETR similarity between the two BoxLists. + + Args: + boxlist1: BoxList holding N groundtruth boxes. + boxlist2: BoxList holding M predicted boxes. + + Returns: + A tensor with shape [N, M] representing pairwise DETR similarity scores. + """ + groundtruth_labels = boxlist1.get_field(fields.BoxListFields.classes) + predicted_labels = boxlist2.get_field(fields.BoxListFields.classes) + classification_scores = tf.matmul(groundtruth_labels, + predicted_labels, + transpose_b=True) + loss = self.l1_weight * box_list_ops.l1( + boxlist1, boxlist2) + self.giou_weight * (1 - box_list_ops.giou( + boxlist1, boxlist2)) - classification_scores + return -loss + + +class NegSqDistSimilarity(RegionSimilarityCalculator): + """Class to compute similarity based on the squared distance metric. + + This class computes pairwise similarity between two BoxLists based on the + negative squared distance metric. + """ + + def _compare(self, boxlist1, boxlist2): + """Compute matrix of (negated) sq distances. + + Args: + boxlist1: BoxList holding N boxes. + boxlist2: BoxList holding M boxes. + + Returns: + A tensor with shape [N, M] representing negated pairwise squared distance. + """ + return -1 * box_list_ops.sq_dist(boxlist1, boxlist2) + + +class IoaSimilarity(RegionSimilarityCalculator): + """Class to compute similarity based on Intersection over Area (IOA) metric. + + This class computes pairwise similarity between two BoxLists based on their + pairwise intersections divided by the areas of second BoxLists. + """ + + def _compare(self, boxlist1, boxlist2): + """Compute pairwise IOA similarity between the two BoxLists. + + Args: + boxlist1: BoxList holding N boxes. + boxlist2: BoxList holding M boxes. + + Returns: + A tensor with shape [N, M] representing pairwise IOA scores. + """ + return box_list_ops.ioa(boxlist1, boxlist2) + + +class ThresholdedIouSimilarity(RegionSimilarityCalculator): + """Class to compute similarity based on thresholded IOU and score. + + This class computes pairwise similarity between two BoxLists based on IOU and + a 'score' present in boxlist1. If IOU > threshold, then the entry in the + output pairwise tensor will contain `score`, otherwise 0. + """ + + def __init__(self, iou_threshold=0): + """Initialize the ThresholdedIouSimilarity. + + Args: + iou_threshold: For a given pair of boxes, if the IOU is > iou_threshold, + then the comparison result will be the foreground probability of + the first box, otherwise it will be zero. + """ + super(ThresholdedIouSimilarity, self).__init__() + self._iou_threshold = iou_threshold + + def _compare(self, boxlist1, boxlist2): + """Compute pairwise IOU similarity between the two BoxLists and score. + + Args: + boxlist1: BoxList holding N boxes. Must have a score field. + boxlist2: BoxList holding M boxes. + + Returns: + A tensor with shape [N, M] representing scores threholded by pairwise + iou scores. + """ + ious = box_list_ops.iou(boxlist1, boxlist2) + scores = boxlist1.get_field(fields.BoxListFields.scores) + scores = tf.expand_dims(scores, axis=1) + row_replicated_scores = tf.tile(scores, [1, tf.shape(ious)[-1]]) + thresholded_ious = tf.where(ious > self._iou_threshold, + row_replicated_scores, tf.zeros_like(ious)) + + return thresholded_ious diff --git a/workspace/virtuallab/object_detection/core/region_similarity_calculator.pyc b/workspace/virtuallab/object_detection/core/region_similarity_calculator.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e32149a84c6125e27f3bd80a67002c2aad94700c GIT binary patch literal 8113 zcmc&(TW=gm6|NcE<7=+IB)h;;T;qVTaS+lfplF?hfTfKi+KB)o)ajY7vD@1-J?X0P zWpTuV^S}dQACZ7Z-qAkrUwGjUAl~=^@O`Ixdd80NW{I6R>6)tQ>Z;RK=X~dUr-%N1 zVdB%@|1oT+;%^wgA3UunJ6};MR%!!fMa5N>R#dC1Hma(^_K>uP)W(o+4@-MkZ4CSN zh)PFPtEM(;zFm{{sM;9y?NODEsf{t28})IuJ)yQI)y9+{1HZNHCVrI1qB zBkX4*jjTVF+@D*2S9PO2x5Wk@=fxMl$JO6Q#VNI2Q3tZ_0V-9sSyhKa>XU2g`H(um z-Kz5a(9!juL871j9L4(l5FWP6c$0fLVVr{1t;prc9*?y=>H$f5=R2S|8h?+s605V0 zo_Cq9Lnn4jo+oi^+BY>-M<$L2RpykrCph6z{ed!VCOc58a#{V8WOK{pdM8S|qGWN> zh#a&sgG%=0g<^BIwG6l1R^(7XP_Gso)=Bq!QrUsWH1t8o876LLPMf_>nluuZ?#pBy zlf!meumx;YV=HNI7OSk}oA$O0(bRqF+4Ak4$a*VFV~o{n-Yyo84!rg9z=uZ%+D6tf zxAmirmYQxyEwiZan`YEaoz>X*8$3f;=?$GU@ycye$Q2mZ&D*7Dk)Cg+S>(QV>!x<5 zZGi){;jO4+^e@*I^uu52-NbF3c>Gv8$Fy=nqJVzIHPItSgK$kZfgeg2ilC0R92Ni~ z%J0MiOxl!Ny`36t+;St-OTO_uDoP6b0JWv9tYwzAv-(mii%n{mvii1ZxG**jKR|^g zO1R`rDC{djIAB7ZeMi{~k#3-BDUrJJUZp{M;6->6>*AeQ!BW_>=B43)fN&CAU_od) z7cqlL%=`mfmOHDeYx9)bZ)hkx9Li>gAvU9%nlO|X3&S{Tpia@L2Ek=i0=8trzNlxt ztpUaRIxbs7QLEIdQ!|cRw3>C-OCQldhx~Cz1wXH$as)pCo&eGzq9xzl{t3lB!AGch z0Vsp!N&t;DeBEl>ncR9!5ERl++Vp|*tB;=Cq}}I9L##po%R_E4pFL6Gdw4hK!UV7V za*v+S7)q_7Vu;po#|umrG$!hb2HAgch=;`ff{^aW>kK&2!G+HmXT+?JfyGJ>A_NkS zfJAOOxv{40h-xLCCs~&Og*zT5%1cTrPi9vi5m1710Z;i0x1)^R!`C+k-VcLfP<=-*> zH2YwzM~`~Hhm!hn91SI7g+5VB2ckROwbX_Ps` z=EqR>1t0AsO*(D%dyQETze_ox=)lV0(n2;lVJYQda{_ zsXRvyHf@y7WP7|H8qTnHf)OikO!jYFpSgB0qOL!$se_uDEaJvXK=7Ba?0>2Wj`cOb zY%|Zg?bzjAL?#Fsdhk+4S|WO@t0t>xh*!(iRV7?OZ zGDrquP~jGw-oDEq?^U?B>#wg#XqiKkh~cxTg)Nf<$Q~96tvtm~kXmLI<4bGbHOJZ7FUDkETQ1(r>NQ&(yX$tbvb`fTN zDvLc`&>3d&3GnX{<`Zm@YH(+Hm8PoE0Olb+M?ekok0|}HPfsLGTlmB(ws#dAB#F}G zV1L8!M*#-6E$PqnvB@*< zE7v?Xry2*}VZo?QsB(BjQ14D9oTlSEdJi3Eo+%VT{(^~pig0YY&&L>g3oic-m17YP z$(V36Bt5o;fRUkrjMYfGJQD-SI;>QR&N1l(?ZN=?F=2Et#dny-uPV?r-k1;?huCUT zeKf4V$JjYlj6NJvo}Cq67`{cj{XY{;$87iWhGF_)7w5heA$p8F1V9x)di+_RPT9`D zZFrgjJ$Py0zOQz$QhU?rwLemlKRM;NZ5$L5Y-0De1McJ z&vrv(-ta*JpF83nC6myg4;wU^bEIc~g&dxqOF5qJuxEjmQmM12I-m`)8r-6EmbGNpf3a_9l)3dlMGM$Z8q xzTje?a!c(DQ%{l(`=S4iaEHrVs(P(*ng6Q1rYcwYJM`5r&P~sKWo~9}=D(s5aeV*) literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/region_similarity_calculator_test.py b/workspace/virtuallab/object_detection/core/region_similarity_calculator_test.py new file mode 100644 index 0000000..ec1de45 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/region_similarity_calculator_test.py @@ -0,0 +1,117 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for region_similarity_calculator.""" +import tensorflow.compat.v1 as tf + +from object_detection.core import box_list +from object_detection.core import region_similarity_calculator +from object_detection.core import standard_fields as fields +from object_detection.utils import test_case + + +class RegionSimilarityCalculatorTest(test_case.TestCase): + + def test_get_correct_pairwise_similarity_based_on_iou(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + iou_similarity_calculator = region_similarity_calculator.IouSimilarity() + iou_similarity = iou_similarity_calculator.compare(boxes1, boxes2) + return iou_similarity + exp_output = [[2.0 / 16.0, 0, 6.0 / 400.0], [1.0 / 16.0, 0.0, 5.0 / 400.0]] + iou_output = self.execute(graph_fn, []) + self.assertAllClose(iou_output, exp_output) + + def test_get_correct_pairwise_similarity_based_on_squared_distances(self): + def graph_fn(): + corners1 = tf.constant([[0.0, 0.0, 0.0, 0.0], + [1.0, 1.0, 0.0, 2.0]]) + corners2 = tf.constant([[3.0, 4.0, 1.0, 0.0], + [-4.0, 0.0, 0.0, 3.0], + [0.0, 0.0, 0.0, 0.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + dist_similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + dist_similarity = dist_similarity_calc.compare(boxes1, boxes2) + return dist_similarity + exp_output = [[-26, -25, 0], [-18, -27, -6]] + iou_output = self.execute(graph_fn, []) + self.assertAllClose(iou_output, exp_output) + + def test_get_correct_pairwise_similarity_based_on_ioa(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + ioa_similarity_calculator = region_similarity_calculator.IoaSimilarity() + ioa_similarity_1 = ioa_similarity_calculator.compare(boxes1, boxes2) + ioa_similarity_2 = ioa_similarity_calculator.compare(boxes2, boxes1) + return ioa_similarity_1, ioa_similarity_2 + exp_output_1 = [[2.0 / 12.0, 0, 6.0 / 400.0], + [1.0 / 12.0, 0.0, 5.0 / 400.0]] + exp_output_2 = [[2.0 / 6.0, 1.0 / 5.0], + [0, 0], + [6.0 / 6.0, 5.0 / 5.0]] + iou_output_1, iou_output_2 = self.execute(graph_fn, []) + self.assertAllClose(iou_output_1, exp_output_1) + self.assertAllClose(iou_output_2, exp_output_2) + + def test_get_correct_pairwise_similarity_based_on_thresholded_iou(self): + def graph_fn(): + corners1 = tf.constant([[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]) + corners2 = tf.constant([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]) + scores = tf.constant([.3, .6]) + iou_threshold = .013 + boxes1 = box_list.BoxList(corners1) + boxes1.add_field(fields.BoxListFields.scores, scores) + boxes2 = box_list.BoxList(corners2) + iou_similarity_calculator = ( + region_similarity_calculator.ThresholdedIouSimilarity( + iou_threshold=iou_threshold)) + iou_similarity = iou_similarity_calculator.compare(boxes1, boxes2) + return iou_similarity + exp_output = tf.constant([[0.3, 0., 0.3], [0.6, 0., 0.]]) + iou_output = self.execute(graph_fn, []) + self.assertAllClose(iou_output, exp_output) + + def test_detr_similarity(self): + def graph_fn(): + corners1 = tf.constant([[5.0, 7.0, 7.0, 9.0]]) + corners2 = tf.constant([[5.0, 7.0, 7.0, 9.0], [5.0, 11.0, 7.0, 13.0]]) + groundtruth_labels = tf.constant([[1.0, 0.0]]) + predicted_labels = tf.constant([[0.0, 1000.0], [1000.0, 0.0]]) + boxes1 = box_list.BoxList(corners1) + boxes2 = box_list.BoxList(corners2) + boxes1.add_field(fields.BoxListFields.classes, groundtruth_labels) + boxes2.add_field(fields.BoxListFields.classes, predicted_labels) + detr_similarity_calculator = \ + region_similarity_calculator.DETRSimilarity() + detr_similarity = detr_similarity_calculator.compare( + boxes1, boxes2, None) + return detr_similarity + exp_output = [[0.0, -20 - 8.0/3.0 + 1000.0]] + sim_output = self.execute(graph_fn, []) + self.assertAllClose(sim_output, exp_output) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/core/standard_fields.py b/workspace/virtuallab/object_detection/core/standard_fields.py new file mode 100644 index 0000000..789296c --- /dev/null +++ b/workspace/virtuallab/object_detection/core/standard_fields.py @@ -0,0 +1,338 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Contains classes specifying naming conventions used for object detection. + + +Specifies: + InputDataFields: standard fields used by reader/preprocessor/batcher. + DetectionResultFields: standard fields returned by object detector. + BoxListFields: standard field used by BoxList + TfExampleFields: standard fields for tf-example data format (go/tf-example). +""" + + +class InputDataFields(object): + """Names for the input tensors. + + Holds the standard data field names to use for identifying input tensors. This + should be used by the decoder to identify keys for the returned tensor_dict + containing input tensors. And it should be used by the model to identify the + tensors it needs. + + Attributes: + image: image. + image_additional_channels: additional channels. + original_image: image in the original input size. + original_image_spatial_shape: image in the original input size. + key: unique key corresponding to image. + source_id: source of the original image. + filename: original filename of the dataset (without common path). + groundtruth_image_classes: image-level class labels. + groundtruth_image_confidences: image-level class confidences. + groundtruth_labeled_classes: image-level annotation that indicates the + classes for which an image has been labeled. + groundtruth_boxes: coordinates of the ground truth boxes in the image. + groundtruth_classes: box-level class labels. + groundtruth_track_ids: box-level track ID labels. + groundtruth_temporal_offset: box-level temporal offsets, i.e., + movement of the box center in adjacent frames. + groundtruth_track_match_flags: box-level flags indicating if objects + exist in the previous frame. + groundtruth_confidences: box-level class confidences. The shape should be + the same as the shape of groundtruth_classes. + groundtruth_label_types: box-level label types (e.g. explicit negative). + groundtruth_is_crowd: [DEPRECATED, use groundtruth_group_of instead] + is the groundtruth a single object or a crowd. + groundtruth_area: area of a groundtruth segment. + groundtruth_difficult: is a `difficult` object + groundtruth_group_of: is a `group_of` objects, e.g. multiple objects of the + same class, forming a connected group, where instances are heavily + occluding each other. + proposal_boxes: coordinates of object proposal boxes. + proposal_objectness: objectness score of each proposal. + groundtruth_instance_masks: ground truth instance masks. + groundtruth_instance_boundaries: ground truth instance boundaries. + groundtruth_instance_classes: instance mask-level class labels. + groundtruth_keypoints: ground truth keypoints. + groundtruth_keypoint_visibilities: ground truth keypoint visibilities. + groundtruth_keypoint_weights: groundtruth weight factor for keypoints. + groundtruth_label_weights: groundtruth label weights. + groundtruth_verified_negative_classes: groundtruth verified negative classes + groundtruth_not_exhaustive_classes: groundtruth not-exhaustively labeled + classes. + groundtruth_weights: groundtruth weight factor for bounding boxes. + groundtruth_dp_num_points: The number of DensePose sampled points for each + instance. + groundtruth_dp_part_ids: Part indices for DensePose points. + groundtruth_dp_surface_coords: Image locations and UV coordinates for + DensePose points. + num_groundtruth_boxes: number of groundtruth boxes. + is_annotated: whether an image has been labeled or not. + true_image_shapes: true shapes of images in the resized images, as resized + images can be padded with zeros. + multiclass_scores: the label score per class for each box. + context_features: a flattened list of contextual features. + context_feature_length: the fixed length of each feature in + context_features, used for reshaping. + valid_context_size: the valid context size, used in filtering the padded + context features. + image_format: format for the images, used to decode + image_height: height of images, used to decode + image_width: width of images, used to decode + """ + image = 'image' + image_additional_channels = 'image_additional_channels' + original_image = 'original_image' + original_image_spatial_shape = 'original_image_spatial_shape' + key = 'key' + source_id = 'source_id' + filename = 'filename' + groundtruth_image_classes = 'groundtruth_image_classes' + groundtruth_image_confidences = 'groundtruth_image_confidences' + groundtruth_labeled_classes = 'groundtruth_labeled_classes' + groundtruth_boxes = 'groundtruth_boxes' + groundtruth_classes = 'groundtruth_classes' + groundtruth_track_ids = 'groundtruth_track_ids' + groundtruth_temporal_offset = 'groundtruth_temporal_offset' + groundtruth_track_match_flags = 'groundtruth_track_match_flags' + groundtruth_confidences = 'groundtruth_confidences' + groundtruth_label_types = 'groundtruth_label_types' + groundtruth_is_crowd = 'groundtruth_is_crowd' + groundtruth_area = 'groundtruth_area' + groundtruth_difficult = 'groundtruth_difficult' + groundtruth_group_of = 'groundtruth_group_of' + proposal_boxes = 'proposal_boxes' + proposal_objectness = 'proposal_objectness' + groundtruth_instance_masks = 'groundtruth_instance_masks' + groundtruth_instance_boundaries = 'groundtruth_instance_boundaries' + groundtruth_instance_classes = 'groundtruth_instance_classes' + groundtruth_keypoints = 'groundtruth_keypoints' + groundtruth_keypoint_visibilities = 'groundtruth_keypoint_visibilities' + groundtruth_keypoint_weights = 'groundtruth_keypoint_weights' + groundtruth_label_weights = 'groundtruth_label_weights' + groundtruth_verified_neg_classes = 'groundtruth_verified_neg_classes' + groundtruth_not_exhaustive_classes = 'groundtruth_not_exhaustive_classes' + groundtruth_weights = 'groundtruth_weights' + groundtruth_dp_num_points = 'groundtruth_dp_num_points' + groundtruth_dp_part_ids = 'groundtruth_dp_part_ids' + groundtruth_dp_surface_coords = 'groundtruth_dp_surface_coords' + num_groundtruth_boxes = 'num_groundtruth_boxes' + is_annotated = 'is_annotated' + true_image_shape = 'true_image_shape' + multiclass_scores = 'multiclass_scores' + context_features = 'context_features' + context_feature_length = 'context_feature_length' + valid_context_size = 'valid_context_size' + image_timestamps = 'image_timestamps' + image_format = 'image_format' + image_height = 'image_height' + image_width = 'image_width' + + +class DetectionResultFields(object): + """Naming conventions for storing the output of the detector. + + Attributes: + source_id: source of the original image. + key: unique key corresponding to image. + detection_boxes: coordinates of the detection boxes in the image. + detection_scores: detection scores for the detection boxes in the image. + detection_multiclass_scores: class score distribution (including background) + for detection boxes in the image including background class. + detection_classes: detection-level class labels. + detection_masks: contains a segmentation mask for each detection box. + detection_surface_coords: contains DensePose surface coordinates for each + box. + detection_boundaries: contains an object boundary for each detection box. + detection_keypoints: contains detection keypoints for each detection box. + detection_keypoint_scores: contains detection keypoint scores. + num_detections: number of detections in the batch. + raw_detection_boxes: contains decoded detection boxes without Non-Max + suppression. + raw_detection_scores: contains class score logits for raw detection boxes. + detection_anchor_indices: The anchor indices of the detections after NMS. + detection_features: contains extracted features for each detected box + after NMS. + """ + + source_id = 'source_id' + key = 'key' + detection_boxes = 'detection_boxes' + detection_scores = 'detection_scores' + detection_multiclass_scores = 'detection_multiclass_scores' + detection_features = 'detection_features' + detection_classes = 'detection_classes' + detection_masks = 'detection_masks' + detection_surface_coords = 'detection_surface_coords' + detection_boundaries = 'detection_boundaries' + detection_keypoints = 'detection_keypoints' + detection_keypoint_scores = 'detection_keypoint_scores' + detection_embeddings = 'detection_embeddings' + detection_offsets = 'detection_temporal_offsets' + num_detections = 'num_detections' + raw_detection_boxes = 'raw_detection_boxes' + raw_detection_scores = 'raw_detection_scores' + detection_anchor_indices = 'detection_anchor_indices' + + +class BoxListFields(object): + """Naming conventions for BoxLists. + + Attributes: + boxes: bounding box coordinates. + classes: classes per bounding box. + scores: scores per bounding box. + weights: sample weights per bounding box. + objectness: objectness score per bounding box. + masks: masks per bounding box. + boundaries: boundaries per bounding box. + keypoints: keypoints per bounding box. + keypoint_visibilities: keypoint visibilities per bounding box. + keypoint_heatmaps: keypoint heatmaps per bounding box. + densepose_num_points: number of DensePose points per bounding box. + densepose_part_ids: DensePose part ids per bounding box. + densepose_surface_coords: DensePose surface coordinates per bounding box. + is_crowd: is_crowd annotation per bounding box. + temporal_offsets: temporal center offsets per bounding box. + track_match_flags: match flags per bounding box. + """ + boxes = 'boxes' + classes = 'classes' + scores = 'scores' + weights = 'weights' + confidences = 'confidences' + objectness = 'objectness' + masks = 'masks' + boundaries = 'boundaries' + keypoints = 'keypoints' + keypoint_visibilities = 'keypoint_visibilities' + keypoint_heatmaps = 'keypoint_heatmaps' + densepose_num_points = 'densepose_num_points' + densepose_part_ids = 'densepose_part_ids' + densepose_surface_coords = 'densepose_surface_coords' + is_crowd = 'is_crowd' + group_of = 'group_of' + track_ids = 'track_ids' + temporal_offsets = 'temporal_offsets' + track_match_flags = 'track_match_flags' + + +class PredictionFields(object): + """Naming conventions for standardized prediction outputs. + + Attributes: + feature_maps: List of feature maps for prediction. + anchors: Generated anchors. + raw_detection_boxes: Decoded detection boxes without NMS. + raw_detection_feature_map_indices: Feature map indices from which each raw + detection box was produced. + """ + feature_maps = 'feature_maps' + anchors = 'anchors' + raw_detection_boxes = 'raw_detection_boxes' + raw_detection_feature_map_indices = 'raw_detection_feature_map_indices' + + +class TfExampleFields(object): + """TF-example proto feature names for object detection. + + Holds the standard feature names to load from an Example proto for object + detection. + + Attributes: + image_encoded: JPEG encoded string + image_format: image format, e.g. "JPEG" + filename: filename + channels: number of channels of image + colorspace: colorspace, e.g. "RGB" + height: height of image in pixels, e.g. 462 + width: width of image in pixels, e.g. 581 + source_id: original source of the image + image_class_text: image-level label in text format + image_class_label: image-level label in numerical format + image_class_confidence: image-level confidence of the label + object_class_text: labels in text format, e.g. ["person", "cat"] + object_class_label: labels in numbers, e.g. [16, 8] + object_bbox_xmin: xmin coordinates of groundtruth box, e.g. 10, 30 + object_bbox_xmax: xmax coordinates of groundtruth box, e.g. 50, 40 + object_bbox_ymin: ymin coordinates of groundtruth box, e.g. 40, 50 + object_bbox_ymax: ymax coordinates of groundtruth box, e.g. 80, 70 + object_view: viewpoint of object, e.g. ["frontal", "left"] + object_truncated: is object truncated, e.g. [true, false] + object_occluded: is object occluded, e.g. [true, false] + object_difficult: is object difficult, e.g. [true, false] + object_group_of: is object a single object or a group of objects + object_depiction: is object a depiction + object_is_crowd: [DEPRECATED, use object_group_of instead] + is the object a single object or a crowd + object_segment_area: the area of the segment. + object_weight: a weight factor for the object's bounding box. + instance_masks: instance segmentation masks. + instance_boundaries: instance boundaries. + instance_classes: Classes for each instance segmentation mask. + detection_class_label: class label in numbers. + detection_bbox_ymin: ymin coordinates of a detection box. + detection_bbox_xmin: xmin coordinates of a detection box. + detection_bbox_ymax: ymax coordinates of a detection box. + detection_bbox_xmax: xmax coordinates of a detection box. + detection_score: detection score for the class label and box. + """ + image_encoded = 'image/encoded' + image_format = 'image/format' # format is reserved keyword + filename = 'image/filename' + channels = 'image/channels' + colorspace = 'image/colorspace' + height = 'image/height' + width = 'image/width' + source_id = 'image/source_id' + image_class_text = 'image/class/text' + image_class_label = 'image/class/label' + image_class_confidence = 'image/class/confidence' + object_class_text = 'image/object/class/text' + object_class_label = 'image/object/class/label' + object_bbox_ymin = 'image/object/bbox/ymin' + object_bbox_xmin = 'image/object/bbox/xmin' + object_bbox_ymax = 'image/object/bbox/ymax' + object_bbox_xmax = 'image/object/bbox/xmax' + object_view = 'image/object/view' + object_truncated = 'image/object/truncated' + object_occluded = 'image/object/occluded' + object_difficult = 'image/object/difficult' + object_group_of = 'image/object/group_of' + object_depiction = 'image/object/depiction' + object_is_crowd = 'image/object/is_crowd' + object_segment_area = 'image/object/segment/area' + object_weight = 'image/object/weight' + instance_masks = 'image/segmentation/object' + instance_boundaries = 'image/boundaries/object' + instance_classes = 'image/segmentation/object/class' + detection_class_label = 'image/detection/label' + detection_bbox_ymin = 'image/detection/bbox/ymin' + detection_bbox_xmin = 'image/detection/bbox/xmin' + detection_bbox_ymax = 'image/detection/bbox/ymax' + detection_bbox_xmax = 'image/detection/bbox/xmax' + detection_score = 'image/detection/score' + +# Sequence fields for SequenceExample inputs. +# All others are considered context fields. +SEQUENCE_FIELDS = [InputDataFields.image, + InputDataFields.source_id, + InputDataFields.groundtruth_boxes, + InputDataFields.num_groundtruth_boxes, + InputDataFields.groundtruth_classes, + InputDataFields.groundtruth_weights, + InputDataFields.source_id, + InputDataFields.is_annotated] diff --git a/workspace/virtuallab/object_detection/core/standard_fields.pyc b/workspace/virtuallab/object_detection/core/standard_fields.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d2db12c178645c6134ac42dcca3337af0c3d833 GIT binary patch literal 15159 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 YWcVIu?acYZ^IPZNJ^#e{xJ{D(0e7mClmGw# literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/target_assigner.py b/workspace/virtuallab/object_detection/core/target_assigner.py new file mode 100644 index 0000000..aec2f5c --- /dev/null +++ b/workspace/virtuallab/object_detection/core/target_assigner.py @@ -0,0 +1,2284 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Base target assigner module. + +The job of a TargetAssigner is, for a given set of anchors (bounding boxes) and +groundtruth detections (bounding boxes), to assign classification and regression +targets to each anchor as well as weights to each anchor (specifying, e.g., +which anchors should not contribute to training loss). + +It assigns classification/regression targets by performing the following steps: +1) Computing pairwise similarity between anchors and groundtruth boxes using a + provided RegionSimilarity Calculator +2) Computing a matching based on the similarity matrix using a provided Matcher +3) Assigning regression targets based on the matching and a provided BoxCoder +4) Assigning classification targets based on the matching and groundtruth labels + +Note that TargetAssigners only operate on detections from a single +image at a time, so any logic for applying a TargetAssigner to multiple +images must be handled externally. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from six.moves import range +from six.moves import zip +import tensorflow.compat.v1 as tf +import tensorflow.compat.v2 as tf2 + +from object_detection.box_coders import faster_rcnn_box_coder +from object_detection.box_coders import mean_stddev_box_coder +from object_detection.core import box_coder +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import densepose_ops +from object_detection.core import keypoint_ops +from object_detection.core import matcher as mat +from object_detection.core import region_similarity_calculator as sim_calc +from object_detection.core import standard_fields as fields +from object_detection.matchers import argmax_matcher +from object_detection.matchers import hungarian_matcher +from object_detection.utils import shape_utils +from object_detection.utils import target_assigner_utils as ta_utils +from object_detection.utils import tf_version + +if tf_version.is_tf1(): + from object_detection.matchers import bipartite_matcher # pylint: disable=g-import-not-at-top + +ResizeMethod = tf2.image.ResizeMethod + +_DEFAULT_KEYPOINT_OFFSET_STD_DEV = 1.0 + + +class TargetAssigner(object): + """Target assigner to compute classification and regression targets.""" + + def __init__(self, + similarity_calc, + matcher, + box_coder_instance, + negative_class_weight=1.0): + """Construct Object Detection Target Assigner. + + Args: + similarity_calc: a RegionSimilarityCalculator + matcher: an object_detection.core.Matcher used to match groundtruth to + anchors. + box_coder_instance: an object_detection.core.BoxCoder used to encode + matching groundtruth boxes with respect to anchors. + negative_class_weight: classification weight to be associated to negative + anchors (default: 1.0). The weight must be in [0., 1.]. + + Raises: + ValueError: if similarity_calc is not a RegionSimilarityCalculator or + if matcher is not a Matcher or if box_coder is not a BoxCoder + """ + if not isinstance(similarity_calc, sim_calc.RegionSimilarityCalculator): + raise ValueError('similarity_calc must be a RegionSimilarityCalculator') + if not isinstance(matcher, mat.Matcher): + raise ValueError('matcher must be a Matcher') + if not isinstance(box_coder_instance, box_coder.BoxCoder): + raise ValueError('box_coder must be a BoxCoder') + self._similarity_calc = similarity_calc + self._matcher = matcher + self._box_coder = box_coder_instance + self._negative_class_weight = negative_class_weight + + @property + def box_coder(self): + return self._box_coder + + # TODO(rathodv): move labels, scores, and weights to groundtruth_boxes fields. + def assign(self, + anchors, + groundtruth_boxes, + groundtruth_labels=None, + unmatched_class_label=None, + groundtruth_weights=None): + """Assign classification and regression targets to each anchor. + + For a given set of anchors and groundtruth detections, match anchors + to groundtruth_boxes and assign classification and regression targets to + each anchor as well as weights based on the resulting match (specifying, + e.g., which anchors should not contribute to training loss). + + Anchors that are not matched to anything are given a classification target + of self._unmatched_cls_target which can be specified via the constructor. + + Args: + anchors: a BoxList representing N anchors + groundtruth_boxes: a BoxList representing M groundtruth boxes + groundtruth_labels: a tensor of shape [M, d_1, ... d_k] + with labels for each of the ground_truth boxes. The subshape + [d_1, ... d_k] can be empty (corresponding to scalar inputs). When set + to None, groundtruth_labels assumes a binary problem where all + ground_truth boxes get a positive label (of 1). + unmatched_class_label: a float32 tensor with shape [d_1, d_2, ..., d_k] + which is consistent with the classification target for each + anchor (and can be empty for scalar targets). This shape must thus be + compatible with the groundtruth labels that are passed to the "assign" + function (which have shape [num_gt_boxes, d_1, d_2, ..., d_k]). + If set to None, unmatched_cls_target is set to be [0] for each anchor. + groundtruth_weights: a float tensor of shape [M] indicating the weight to + assign to all anchors match to a particular groundtruth box. The weights + must be in [0., 1.]. If None, all weights are set to 1. Generally no + groundtruth boxes with zero weight match to any anchors as matchers are + aware of groundtruth weights. Additionally, `cls_weights` and + `reg_weights` are calculated using groundtruth weights as an added + safety. + + Returns: + cls_targets: a float32 tensor with shape [num_anchors, d_1, d_2 ... d_k], + where the subshape [d_1, ..., d_k] is compatible with groundtruth_labels + which has shape [num_gt_boxes, d_1, d_2, ... d_k]. + cls_weights: a float32 tensor with shape [num_anchors, d_1, d_2 ... d_k], + representing weights for each element in cls_targets. + reg_targets: a float32 tensor with shape [num_anchors, box_code_dimension] + reg_weights: a float32 tensor with shape [num_anchors] + match: an int32 tensor of shape [num_anchors] containing result of anchor + groundtruth matching. Each position in the tensor indicates an anchor + and holds the following meaning: + (1) if match[i] >= 0, anchor i is matched with groundtruth match[i]. + (2) if match[i]=-1, anchor i is marked to be background . + (3) if match[i]=-2, anchor i is ignored since it is not background and + does not have sufficient overlap to call it a foreground. + + Raises: + ValueError: if anchors or groundtruth_boxes are not of type + box_list.BoxList + """ + if not isinstance(anchors, box_list.BoxList): + raise ValueError('anchors must be an BoxList') + if not isinstance(groundtruth_boxes, box_list.BoxList): + raise ValueError('groundtruth_boxes must be an BoxList') + + if unmatched_class_label is None: + unmatched_class_label = tf.constant([0], tf.float32) + + if groundtruth_labels is None: + groundtruth_labels = tf.ones(tf.expand_dims(groundtruth_boxes.num_boxes(), + 0)) + groundtruth_labels = tf.expand_dims(groundtruth_labels, -1) + + unmatched_shape_assert = shape_utils.assert_shape_equal( + shape_utils.combined_static_and_dynamic_shape(groundtruth_labels)[1:], + shape_utils.combined_static_and_dynamic_shape(unmatched_class_label)) + labels_and_box_shapes_assert = shape_utils.assert_shape_equal( + shape_utils.combined_static_and_dynamic_shape( + groundtruth_labels)[:1], + shape_utils.combined_static_and_dynamic_shape( + groundtruth_boxes.get())[:1]) + + if groundtruth_weights is None: + num_gt_boxes = groundtruth_boxes.num_boxes_static() + if not num_gt_boxes: + num_gt_boxes = groundtruth_boxes.num_boxes() + groundtruth_weights = tf.ones([num_gt_boxes], dtype=tf.float32) + + # set scores on the gt boxes + scores = 1 - groundtruth_labels[:, 0] + groundtruth_boxes.add_field(fields.BoxListFields.scores, scores) + + with tf.control_dependencies( + [unmatched_shape_assert, labels_and_box_shapes_assert]): + match_quality_matrix = self._similarity_calc.compare(groundtruth_boxes, + anchors) + match = self._matcher.match(match_quality_matrix, + valid_rows=tf.greater(groundtruth_weights, 0)) + reg_targets = self._create_regression_targets(anchors, + groundtruth_boxes, + match) + cls_targets = self._create_classification_targets(groundtruth_labels, + unmatched_class_label, + match) + reg_weights = self._create_regression_weights(match, groundtruth_weights) + + cls_weights = self._create_classification_weights(match, + groundtruth_weights) + # convert cls_weights from per-anchor to per-class. + class_label_shape = tf.shape(cls_targets)[1:] + weights_shape = tf.shape(cls_weights) + weights_multiple = tf.concat( + [tf.ones_like(weights_shape), class_label_shape], + axis=0) + for _ in range(len(cls_targets.get_shape()[1:])): + cls_weights = tf.expand_dims(cls_weights, -1) + cls_weights = tf.tile(cls_weights, weights_multiple) + + num_anchors = anchors.num_boxes_static() + if num_anchors is not None: + reg_targets = self._reset_target_shape(reg_targets, num_anchors) + cls_targets = self._reset_target_shape(cls_targets, num_anchors) + reg_weights = self._reset_target_shape(reg_weights, num_anchors) + cls_weights = self._reset_target_shape(cls_weights, num_anchors) + + return (cls_targets, cls_weights, reg_targets, reg_weights, + match.match_results) + + def _reset_target_shape(self, target, num_anchors): + """Sets the static shape of the target. + + Args: + target: the target tensor. Its first dimension will be overwritten. + num_anchors: the number of anchors, which is used to override the target's + first dimension. + + Returns: + A tensor with the shape info filled in. + """ + target_shape = target.get_shape().as_list() + target_shape[0] = num_anchors + target.set_shape(target_shape) + return target + + def _create_regression_targets(self, anchors, groundtruth_boxes, match): + """Returns a regression target for each anchor. + + Args: + anchors: a BoxList representing N anchors + groundtruth_boxes: a BoxList representing M groundtruth_boxes + match: a matcher.Match object + + Returns: + reg_targets: a float32 tensor with shape [N, box_code_dimension] + """ + matched_gt_boxes = match.gather_based_on_match( + groundtruth_boxes.get(), + unmatched_value=tf.zeros(4), + ignored_value=tf.zeros(4)) + matched_gt_boxlist = box_list.BoxList(matched_gt_boxes) + if groundtruth_boxes.has_field(fields.BoxListFields.keypoints): + groundtruth_keypoints = groundtruth_boxes.get_field( + fields.BoxListFields.keypoints) + matched_keypoints = match.gather_based_on_match( + groundtruth_keypoints, + unmatched_value=tf.zeros(groundtruth_keypoints.get_shape()[1:]), + ignored_value=tf.zeros(groundtruth_keypoints.get_shape()[1:])) + matched_gt_boxlist.add_field(fields.BoxListFields.keypoints, + matched_keypoints) + matched_reg_targets = self._box_coder.encode(matched_gt_boxlist, anchors) + match_results_shape = shape_utils.combined_static_and_dynamic_shape( + match.match_results) + + # Zero out the unmatched and ignored regression targets. + unmatched_ignored_reg_targets = tf.tile( + self._default_regression_target(), [match_results_shape[0], 1]) + matched_anchors_mask = match.matched_column_indicator() + reg_targets = tf.where(matched_anchors_mask, + matched_reg_targets, + unmatched_ignored_reg_targets) + return reg_targets + + def _default_regression_target(self): + """Returns the default target for anchors to regress to. + + Default regression targets are set to zero (though in + this implementation what these targets are set to should + not matter as the regression weight of any box set to + regress to the default target is zero). + + Returns: + default_target: a float32 tensor with shape [1, box_code_dimension] + """ + return tf.constant([self._box_coder.code_size*[0]], tf.float32) + + def _create_classification_targets(self, groundtruth_labels, + unmatched_class_label, match): + """Create classification targets for each anchor. + + Assign a classification target of for each anchor to the matching + groundtruth label that is provided by match. Anchors that are not matched + to anything are given the target self._unmatched_cls_target + + Args: + groundtruth_labels: a tensor of shape [num_gt_boxes, d_1, ... d_k] + with labels for each of the ground_truth boxes. The subshape + [d_1, ... d_k] can be empty (corresponding to scalar labels). + unmatched_class_label: a float32 tensor with shape [d_1, d_2, ..., d_k] + which is consistent with the classification target for each + anchor (and can be empty for scalar targets). This shape must thus be + compatible with the groundtruth labels that are passed to the "assign" + function (which have shape [num_gt_boxes, d_1, d_2, ..., d_k]). + match: a matcher.Match object that provides a matching between anchors + and groundtruth boxes. + + Returns: + a float32 tensor with shape [num_anchors, d_1, d_2 ... d_k], where the + subshape [d_1, ..., d_k] is compatible with groundtruth_labels which has + shape [num_gt_boxes, d_1, d_2, ... d_k]. + """ + return match.gather_based_on_match( + groundtruth_labels, + unmatched_value=unmatched_class_label, + ignored_value=unmatched_class_label) + + def _create_regression_weights(self, match, groundtruth_weights): + """Set regression weight for each anchor. + + Only positive anchors are set to contribute to the regression loss, so this + method returns a weight of 1 for every positive anchor and 0 for every + negative anchor. + + Args: + match: a matcher.Match object that provides a matching between anchors + and groundtruth boxes. + groundtruth_weights: a float tensor of shape [M] indicating the weight to + assign to all anchors match to a particular groundtruth box. + + Returns: + a float32 tensor with shape [num_anchors] representing regression weights. + """ + return match.gather_based_on_match( + groundtruth_weights, ignored_value=0., unmatched_value=0.) + + def _create_classification_weights(self, + match, + groundtruth_weights): + """Create classification weights for each anchor. + + Positive (matched) anchors are associated with a weight of + positive_class_weight and negative (unmatched) anchors are associated with + a weight of negative_class_weight. When anchors are ignored, weights are set + to zero. By default, both positive/negative weights are set to 1.0, + but they can be adjusted to handle class imbalance (which is almost always + the case in object detection). + + Args: + match: a matcher.Match object that provides a matching between anchors + and groundtruth boxes. + groundtruth_weights: a float tensor of shape [M] indicating the weight to + assign to all anchors match to a particular groundtruth box. + + Returns: + a float32 tensor with shape [num_anchors] representing classification + weights. + """ + return match.gather_based_on_match( + groundtruth_weights, + ignored_value=0., + unmatched_value=self._negative_class_weight) + + def get_box_coder(self): + """Get BoxCoder of this TargetAssigner. + + Returns: + BoxCoder object. + """ + return self._box_coder + + +# TODO(rathodv): This method pulls in all the implementation dependencies into +# core. Therefore its best to have this factory method outside of core. +def create_target_assigner(reference, stage=None, + negative_class_weight=1.0, use_matmul_gather=False): + """Factory function for creating standard target assigners. + + Args: + reference: string referencing the type of TargetAssigner. + stage: string denoting stage: {proposal, detection}. + negative_class_weight: classification weight to be associated to negative + anchors (default: 1.0) + use_matmul_gather: whether to use matrix multiplication based gather which + are better suited for TPUs. + + Returns: + TargetAssigner: desired target assigner. + + Raises: + ValueError: if combination reference+stage is invalid. + """ + if reference == 'Multibox' and stage == 'proposal': + if tf_version.is_tf2(): + raise ValueError('GreedyBipartiteMatcher is not supported in TF 2.X.') + similarity_calc = sim_calc.NegSqDistSimilarity() + matcher = bipartite_matcher.GreedyBipartiteMatcher() + box_coder_instance = mean_stddev_box_coder.MeanStddevBoxCoder() + + elif reference == 'FasterRCNN' and stage == 'proposal': + similarity_calc = sim_calc.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.7, + unmatched_threshold=0.3, + force_match_for_each_row=True, + use_matmul_gather=use_matmul_gather) + box_coder_instance = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=[10.0, 10.0, 5.0, 5.0]) + + elif reference == 'FasterRCNN' and stage == 'detection': + similarity_calc = sim_calc.IouSimilarity() + # Uses all proposals with IOU < 0.5 as candidate negatives. + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + negatives_lower_than_unmatched=True, + use_matmul_gather=use_matmul_gather) + box_coder_instance = faster_rcnn_box_coder.FasterRcnnBoxCoder( + scale_factors=[10.0, 10.0, 5.0, 5.0]) + + elif reference == 'FastRCNN': + similarity_calc = sim_calc.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.1, + force_match_for_each_row=False, + negatives_lower_than_unmatched=False, + use_matmul_gather=use_matmul_gather) + box_coder_instance = faster_rcnn_box_coder.FasterRcnnBoxCoder() + + else: + raise ValueError('No valid combination of reference and stage.') + + return TargetAssigner(similarity_calc, matcher, box_coder_instance, + negative_class_weight=negative_class_weight) + + +def batch_assign(target_assigner, + anchors_batch, + gt_box_batch, + gt_class_targets_batch, + unmatched_class_label=None, + gt_weights_batch=None): + """Batched assignment of classification and regression targets. + + Args: + target_assigner: a target assigner. + anchors_batch: BoxList representing N box anchors or list of BoxList objects + with length batch_size representing anchor sets. + gt_box_batch: a list of BoxList objects with length batch_size + representing groundtruth boxes for each image in the batch + gt_class_targets_batch: a list of tensors with length batch_size, where + each tensor has shape [num_gt_boxes_i, classification_target_size] and + num_gt_boxes_i is the number of boxes in the ith boxlist of + gt_box_batch. + unmatched_class_label: a float32 tensor with shape [d_1, d_2, ..., d_k] + which is consistent with the classification target for each + anchor (and can be empty for scalar targets). This shape must thus be + compatible with the groundtruth labels that are passed to the "assign" + function (which have shape [num_gt_boxes, d_1, d_2, ..., d_k]). + gt_weights_batch: A list of 1-D tf.float32 tensors of shape + [num_boxes] containing weights for groundtruth boxes. + + Returns: + batch_cls_targets: a tensor with shape [batch_size, num_anchors, + num_classes], + batch_cls_weights: a tensor with shape [batch_size, num_anchors, + num_classes], + batch_reg_targets: a tensor with shape [batch_size, num_anchors, + box_code_dimension] + batch_reg_weights: a tensor with shape [batch_size, num_anchors], + match: an int32 tensor of shape [batch_size, num_anchors] containing result + of anchor groundtruth matching. Each position in the tensor indicates an + anchor and holds the following meaning: + (1) if match[x, i] >= 0, anchor i is matched with groundtruth match[x, i]. + (2) if match[x, i]=-1, anchor i is marked to be background . + (3) if match[x, i]=-2, anchor i is ignored since it is not background and + does not have sufficient overlap to call it a foreground. + + Raises: + ValueError: if input list lengths are inconsistent, i.e., + batch_size == len(gt_box_batch) == len(gt_class_targets_batch) + and batch_size == len(anchors_batch) unless anchors_batch is a single + BoxList. + """ + if not isinstance(anchors_batch, list): + anchors_batch = len(gt_box_batch) * [anchors_batch] + if not all( + isinstance(anchors, box_list.BoxList) for anchors in anchors_batch): + raise ValueError('anchors_batch must be a BoxList or list of BoxLists.') + if not (len(anchors_batch) + == len(gt_box_batch) + == len(gt_class_targets_batch)): + raise ValueError('batch size incompatible with lengths of anchors_batch, ' + 'gt_box_batch and gt_class_targets_batch.') + cls_targets_list = [] + cls_weights_list = [] + reg_targets_list = [] + reg_weights_list = [] + match_list = [] + if gt_weights_batch is None: + gt_weights_batch = [None] * len(gt_class_targets_batch) + for anchors, gt_boxes, gt_class_targets, gt_weights in zip( + anchors_batch, gt_box_batch, gt_class_targets_batch, gt_weights_batch): + (cls_targets, cls_weights, + reg_targets, reg_weights, match) = target_assigner.assign( + anchors, gt_boxes, gt_class_targets, unmatched_class_label, + gt_weights) + cls_targets_list.append(cls_targets) + cls_weights_list.append(cls_weights) + reg_targets_list.append(reg_targets) + reg_weights_list.append(reg_weights) + match_list.append(match) + batch_cls_targets = tf.stack(cls_targets_list) + batch_cls_weights = tf.stack(cls_weights_list) + batch_reg_targets = tf.stack(reg_targets_list) + batch_reg_weights = tf.stack(reg_weights_list) + batch_match = tf.stack(match_list) + return (batch_cls_targets, batch_cls_weights, batch_reg_targets, + batch_reg_weights, batch_match) + + +# Assign an alias to avoid large refactor of existing users. +batch_assign_targets = batch_assign + + +def batch_get_targets(batch_match, groundtruth_tensor_list, + groundtruth_weights_list, unmatched_value, + unmatched_weight): + """Returns targets based on anchor-groundtruth box matching results. + + Args: + batch_match: An int32 tensor of shape [batch, num_anchors] containing the + result of target assignment returned by TargetAssigner.assign(..). + groundtruth_tensor_list: A list of groundtruth tensors of shape + [num_groundtruth, d_1, d_2, ..., d_k]. The tensors can be of any type. + groundtruth_weights_list: A list of weights, one per groundtruth tensor, of + shape [num_groundtruth]. + unmatched_value: A tensor of shape [d_1, d_2, ..., d_k] of the same type as + groundtruth tensor containing target value for anchors that remain + unmatched. + unmatched_weight: Scalar weight to assign to anchors that remain unmatched. + + Returns: + targets: A tensor of shape [batch, num_anchors, d_1, d_2, ..., d_k] + containing targets for anchors. + weights: A float tensor of shape [batch, num_anchors] containing the weights + to assign to each target. + """ + match_list = tf.unstack(batch_match) + targets_list = [] + weights_list = [] + for match_tensor, groundtruth_tensor, groundtruth_weight in zip( + match_list, groundtruth_tensor_list, groundtruth_weights_list): + match_object = mat.Match(match_tensor) + targets = match_object.gather_based_on_match( + groundtruth_tensor, + unmatched_value=unmatched_value, + ignored_value=unmatched_value) + targets_list.append(targets) + weights = match_object.gather_based_on_match( + groundtruth_weight, + unmatched_value=unmatched_weight, + ignored_value=tf.zeros_like(unmatched_weight)) + weights_list.append(weights) + return tf.stack(targets_list), tf.stack(weights_list) + + +def batch_assign_confidences(target_assigner, + anchors_batch, + gt_box_batch, + gt_class_confidences_batch, + gt_weights_batch=None, + unmatched_class_label=None, + include_background_class=True, + implicit_class_weight=1.0): + """Batched assignment of classification and regression targets. + + This differences between batch_assign_confidences and batch_assign_targets: + - 'batch_assign_targets' supports scalar (agnostic), vector (multiclass) and + tensor (high-dimensional) targets. 'batch_assign_confidences' only support + scalar (agnostic) and vector (multiclass) targets. + - 'batch_assign_targets' assumes the input class tensor using the binary + one/K-hot encoding. 'batch_assign_confidences' takes the class confidence + scores as the input, where 1 means positive classes, 0 means implicit + negative classes, and -1 means explicit negative classes. + - 'batch_assign_confidences' assigns the targets in the similar way as + 'batch_assign_targets' except that it gives different weights for implicit + and explicit classes. This allows user to control the negative gradients + pushed differently for implicit and explicit examples during the training. + + Args: + target_assigner: a target assigner. + anchors_batch: BoxList representing N box anchors or list of BoxList objects + with length batch_size representing anchor sets. + gt_box_batch: a list of BoxList objects with length batch_size + representing groundtruth boxes for each image in the batch + gt_class_confidences_batch: a list of tensors with length batch_size, where + each tensor has shape [num_gt_boxes_i, classification_target_size] and + num_gt_boxes_i is the number of boxes in the ith boxlist of + gt_box_batch. Note that in this tensor, 1 means explicit positive class, + -1 means explicit negative class, and 0 means implicit negative class. + gt_weights_batch: A list of 1-D tf.float32 tensors of shape + [num_gt_boxes_i] containing weights for groundtruth boxes. + unmatched_class_label: a float32 tensor with shape [d_1, d_2, ..., d_k] + which is consistent with the classification target for each + anchor (and can be empty for scalar targets). This shape must thus be + compatible with the groundtruth labels that are passed to the "assign" + function (which have shape [num_gt_boxes, d_1, d_2, ..., d_k]). + include_background_class: whether or not gt_class_confidences_batch includes + the background class. + implicit_class_weight: the weight assigned to implicit examples. + + Returns: + batch_cls_targets: a tensor with shape [batch_size, num_anchors, + num_classes], + batch_cls_weights: a tensor with shape [batch_size, num_anchors, + num_classes], + batch_reg_targets: a tensor with shape [batch_size, num_anchors, + box_code_dimension] + batch_reg_weights: a tensor with shape [batch_size, num_anchors], + match: an int32 tensor of shape [batch_size, num_anchors] containing result + of anchor groundtruth matching. Each position in the tensor indicates an + anchor and holds the following meaning: + (1) if match[x, i] >= 0, anchor i is matched with groundtruth match[x, i]. + (2) if match[x, i]=-1, anchor i is marked to be background . + (3) if match[x, i]=-2, anchor i is ignored since it is not background and + does not have sufficient overlap to call it a foreground. + + Raises: + ValueError: if input list lengths are inconsistent, i.e., + batch_size == len(gt_box_batch) == len(gt_class_targets_batch) + and batch_size == len(anchors_batch) unless anchors_batch is a single + BoxList, or if any element in gt_class_confidences_batch has rank > 2. + """ + if not isinstance(anchors_batch, list): + anchors_batch = len(gt_box_batch) * [anchors_batch] + if not all( + isinstance(anchors, box_list.BoxList) for anchors in anchors_batch): + raise ValueError('anchors_batch must be a BoxList or list of BoxLists.') + if not (len(anchors_batch) + == len(gt_box_batch) + == len(gt_class_confidences_batch)): + raise ValueError('batch size incompatible with lengths of anchors_batch, ' + 'gt_box_batch and gt_class_confidences_batch.') + + cls_targets_list = [] + cls_weights_list = [] + reg_targets_list = [] + reg_weights_list = [] + match_list = [] + if gt_weights_batch is None: + gt_weights_batch = [None] * len(gt_class_confidences_batch) + for anchors, gt_boxes, gt_class_confidences, gt_weights in zip( + anchors_batch, gt_box_batch, gt_class_confidences_batch, + gt_weights_batch): + + if (gt_class_confidences is not None and + len(gt_class_confidences.get_shape().as_list()) > 2): + raise ValueError('The shape of the class target is not supported. ', + gt_class_confidences.get_shape()) + + cls_targets, _, reg_targets, _, match = target_assigner.assign( + anchors, gt_boxes, gt_class_confidences, unmatched_class_label, + groundtruth_weights=gt_weights) + + if include_background_class: + cls_targets_without_background = tf.slice( + cls_targets, [0, 1], [-1, -1]) + else: + cls_targets_without_background = cls_targets + + positive_mask = tf.greater(cls_targets_without_background, 0.0) + negative_mask = tf.less(cls_targets_without_background, 0.0) + explicit_example_mask = tf.logical_or(positive_mask, negative_mask) + positive_anchors = tf.reduce_any(positive_mask, axis=-1) + + regression_weights = tf.cast(positive_anchors, dtype=tf.float32) + regression_targets = ( + reg_targets * tf.expand_dims(regression_weights, axis=-1)) + regression_weights_expanded = tf.expand_dims(regression_weights, axis=-1) + + cls_targets_without_background = ( + cls_targets_without_background * + (1 - tf.cast(negative_mask, dtype=tf.float32))) + cls_weights_without_background = ((1 - implicit_class_weight) * tf.cast( + explicit_example_mask, dtype=tf.float32) + implicit_class_weight) + + if include_background_class: + cls_weights_background = ( + (1 - implicit_class_weight) * regression_weights_expanded + + implicit_class_weight) + classification_weights = tf.concat( + [cls_weights_background, cls_weights_without_background], axis=-1) + cls_targets_background = 1 - regression_weights_expanded + classification_targets = tf.concat( + [cls_targets_background, cls_targets_without_background], axis=-1) + else: + classification_targets = cls_targets_without_background + classification_weights = cls_weights_without_background + + cls_targets_list.append(classification_targets) + cls_weights_list.append(classification_weights) + reg_targets_list.append(regression_targets) + reg_weights_list.append(regression_weights) + match_list.append(match) + batch_cls_targets = tf.stack(cls_targets_list) + batch_cls_weights = tf.stack(cls_weights_list) + batch_reg_targets = tf.stack(reg_targets_list) + batch_reg_weights = tf.stack(reg_weights_list) + batch_match = tf.stack(match_list) + return (batch_cls_targets, batch_cls_weights, batch_reg_targets, + batch_reg_weights, batch_match) + + +def _smallest_positive_root(a, b, c): + """Returns the smallest positive root of a quadratic equation.""" + + discriminant = tf.sqrt(b ** 2 - 4 * a * c) + + # TODO(vighneshb) We are currently using the slightly incorrect + # CenterNet implementation. The commented lines implement the fixed version + # in https://github.com/princeton-vl/CornerNet. Change the implementation + # after verifying it has no negative impact. + # root1 = (-b - discriminant) / (2 * a) + # root2 = (-b + discriminant) / (2 * a) + + # return tf.where(tf.less(root1, 0), root2, root1) + + return (-b + discriminant) / (2.0) + + +def max_distance_for_overlap(height, width, min_iou): + """Computes how far apart bbox corners can lie while maintaining the iou. + + Given a bounding box size, this function returns a lower bound on how far + apart the corners of another box can lie while still maintaining the given + IoU. The implementation is based on the `gaussian_radius` function in the + Objects as Points github repo: https://github.com/xingyizhou/CenterNet + + Args: + height: A 1-D float Tensor representing height of the ground truth boxes. + width: A 1-D float Tensor representing width of the ground truth boxes. + min_iou: A float representing the minimum IoU desired. + + Returns: + distance: A 1-D Tensor of distances, of the same length as the input + height and width tensors. + """ + + # Given that the detected box is displaced at a distance `d`, the exact + # IoU value will depend on the angle at which each corner is displaced. + # We simplify our computation by assuming that each corner is displaced by + # a distance `d` in both x and y direction. This gives us a lower IoU than + # what is actually realizable and ensures that any box with corners less + # than `d` distance apart will always have an IoU greater than or equal + # to `min_iou` + + # The following 3 cases can be worked on geometrically and come down to + # solving a quadratic inequality. In each case, to ensure `min_iou` we use + # the smallest positive root of the equation. + + # Case where detected box is offset from ground truth and no box completely + # contains the other. + + distance_detection_offset = _smallest_positive_root( + a=1, b=-(height + width), + c=width * height * ((1 - min_iou) / (1 + min_iou)) + ) + + # Case where detection is smaller than ground truth and completely contained + # in it. + distance_detection_in_gt = _smallest_positive_root( + a=4, b=-2 * (height + width), + c=(1 - min_iou) * width * height + ) + + # Case where ground truth is smaller than detection and completely contained + # in it. + distance_gt_in_detection = _smallest_positive_root( + a=4 * min_iou, b=(2 * min_iou) * (width + height), + c=(min_iou - 1) * width * height + ) + + return tf.reduce_min([distance_detection_offset, + distance_gt_in_detection, + distance_detection_in_gt], axis=0) + + +def get_batch_predictions_from_indices(batch_predictions, indices): + """Gets the values of predictions in a batch at the given indices. + + The indices are expected to come from the offset targets generation functions + in this library. The returned value is intended to be used inside a loss + function. + + Args: + batch_predictions: A tensor of shape [batch_size, height, width, channels] + or [batch_size, height, width, class, channels] for class-specific + features (e.g. keypoint joint offsets). + indices: A tensor of shape [num_instances, 3] for single class features or + [num_instances, 4] for multiple classes features. + + Returns: + values: A tensor of shape [num_instances, channels] holding the predicted + values at the given indices. + """ + return tf.gather_nd(batch_predictions, indices) + + +def _compute_std_dev_from_box_size(boxes_height, boxes_width, min_overlap): + """Computes the standard deviation of the Gaussian kernel from box size. + + Args: + boxes_height: A 1D tensor with shape [num_instances] representing the height + of each box. + boxes_width: A 1D tensor with shape [num_instances] representing the width + of each box. + min_overlap: The minimum IOU overlap that boxes need to have to not be + penalized. + + Returns: + A 1D tensor with shape [num_instances] representing the computed Gaussian + sigma for each of the box. + """ + # We are dividing by 3 so that points closer than the computed + # distance have a >99% CDF. + sigma = max_distance_for_overlap(boxes_height, boxes_width, min_overlap) + sigma = (2 * tf.math.maximum(tf.math.floor(sigma), 0.0) + 1) / 6.0 + return sigma + + +class CenterNetCenterHeatmapTargetAssigner(object): + """Wrapper to compute the object center heatmap.""" + + def __init__(self, stride, min_overlap=0.7, compute_heatmap_sparse=False): + """Initializes the target assigner. + + Args: + stride: int, the stride of the network in output pixels. + min_overlap: The minimum IOU overlap that boxes need to have to not be + penalized. + compute_heatmap_sparse: bool, indicating whether or not to use the sparse + version of the Op that computes the heatmap. The sparse version scales + better with number of classes, but in some cases is known to cause + OOM error. See (b/170989061). + """ + + self._stride = stride + self._min_overlap = min_overlap + self._compute_heatmap_sparse = compute_heatmap_sparse + + def assign_center_targets_from_boxes(self, + height, + width, + gt_boxes_list, + gt_classes_list, + gt_weights_list=None): + """Computes the object center heatmap target. + + Args: + height: int, height of input to the model. This is used to + determine the height of the output. + width: int, width of the input to the model. This is used to + determine the width of the output. + gt_boxes_list: A list of float tensors with shape [num_boxes, 4] + representing the groundtruth detection bounding boxes for each sample in + the batch. The box coordinates are expected in normalized coordinates. + gt_classes_list: A list of float tensors with shape [num_boxes, + num_classes] representing the one-hot encoded class labels for each box + in the gt_boxes_list. + gt_weights_list: A list of float tensors with shape [num_boxes] + representing the weight of each groundtruth detection box. + + Returns: + heatmap: A Tensor of size [batch_size, output_height, output_width, + num_classes] representing the per class center heatmap. output_height + and output_width are computed by dividing the input height and width by + the stride specified during initialization. + """ + + out_height = tf.cast(height // self._stride, tf.float32) + out_width = tf.cast(width // self._stride, tf.float32) + # Compute the yx-grid to be used to generate the heatmap. Each returned + # tensor has shape of [out_height, out_width] + (y_grid, x_grid) = ta_utils.image_shape_to_grids(out_height, out_width) + + heatmaps = [] + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_boxes_list) + # TODO(vighneshb) Replace the for loop with a batch version. + for boxes, class_targets, weights in zip(gt_boxes_list, gt_classes_list, + gt_weights_list): + boxes = box_list.BoxList(boxes) + # Convert the box coordinates to absolute output image dimension space. + boxes = box_list_ops.to_absolute_coordinates(boxes, + height // self._stride, + width // self._stride) + # Get the box center coordinates. Each returned tensors have the shape of + # [num_instances] + (y_center, x_center, boxes_height, + boxes_width) = boxes.get_center_coordinates_and_sizes() + + # Compute the sigma from box size. The tensor shape: [num_instances]. + sigma = _compute_std_dev_from_box_size(boxes_height, boxes_width, + self._min_overlap) + # Apply the Gaussian kernel to the center coordinates. Returned heatmap + # has shape of [out_height, out_width, num_classes] + heatmap = ta_utils.coordinates_to_heatmap( + y_grid=y_grid, + x_grid=x_grid, + y_coordinates=y_center, + x_coordinates=x_center, + sigma=sigma, + channel_onehot=class_targets, + channel_weights=weights, + sparse=self._compute_heatmap_sparse) + heatmaps.append(heatmap) + + # Return the stacked heatmaps over the batch. + return tf.stack(heatmaps, axis=0) + + +class CenterNetBoxTargetAssigner(object): + """Wrapper to compute target tensors for the object detection task. + + This class has methods that take as input a batch of ground truth tensors + (in the form of a list) and return the targets required to train the object + detection task. + """ + + def __init__(self, stride): + """Initializes the target assigner. + + Args: + stride: int, the stride of the network in output pixels. + """ + + self._stride = stride + + def assign_size_and_offset_targets(self, + height, + width, + gt_boxes_list, + gt_weights_list=None): + """Returns the box height/width and center offset targets and their indices. + + The returned values are expected to be used with predicted tensors + of size (batch_size, height//self._stride, width//self._stride, 2). The + predicted values at the relevant indices can be retrieved with the + get_batch_predictions_from_indices function. + + Args: + height: int, height of input to the model. This is used to determine the + height of the output. + width: int, width of the input to the model. This is used to determine the + width of the output. + gt_boxes_list: A list of float tensors with shape [num_boxes, 4] + representing the groundtruth detection bounding boxes for each sample in + the batch. The coordinates are expected in normalized coordinates. + gt_weights_list: A list of tensors with shape [num_boxes] corresponding to + the weight of each groundtruth detection box. + + Returns: + batch_indices: an integer tensor of shape [num_boxes, 3] holding the + indices inside the predicted tensor which should be penalized. The + first column indicates the index along the batch dimension and the + second and third columns indicate the index along the y and x + dimensions respectively. + batch_box_height_width: a float tensor of shape [num_boxes, 2] holding + expected height and width of each box in the output space. + batch_offsets: a float tensor of shape [num_boxes, 2] holding the + expected y and x offset of each box in the output space. + batch_weights: a float tensor of shape [num_boxes] indicating the + weight of each prediction. + """ + + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_boxes_list) + + batch_indices = [] + batch_box_height_width = [] + batch_weights = [] + batch_offsets = [] + + for i, (boxes, weights) in enumerate(zip(gt_boxes_list, gt_weights_list)): + boxes = box_list.BoxList(boxes) + boxes = box_list_ops.to_absolute_coordinates(boxes, + height // self._stride, + width // self._stride) + # Get the box center coordinates. Each returned tensors have the shape of + # [num_boxes] + (y_center, x_center, boxes_height, + boxes_width) = boxes.get_center_coordinates_and_sizes() + num_boxes = tf.shape(x_center) + + # Compute the offsets and indices of the box centers. Shape: + # offsets: [num_boxes, 2] + # indices: [num_boxes, 2] + (offsets, indices) = ta_utils.compute_floor_offsets_with_indices( + y_source=y_center, x_source=x_center) + + # Assign ones if weights are not provided. + if weights is None: + weights = tf.ones(num_boxes, dtype=tf.float32) + + # Shape of [num_boxes, 1] integer tensor filled with current batch index. + batch_index = i * tf.ones_like(indices[:, 0:1], dtype=tf.int32) + batch_indices.append(tf.concat([batch_index, indices], axis=1)) + batch_box_height_width.append( + tf.stack([boxes_height, boxes_width], axis=1)) + batch_weights.append(weights) + batch_offsets.append(offsets) + + batch_indices = tf.concat(batch_indices, axis=0) + batch_box_height_width = tf.concat(batch_box_height_width, axis=0) + batch_weights = tf.concat(batch_weights, axis=0) + batch_offsets = tf.concat(batch_offsets, axis=0) + return (batch_indices, batch_box_height_width, batch_offsets, batch_weights) + + +# TODO(yuhuic): Update this class to handle the instance/keypoint weights. +# Currently those weights are used as "mask" to indicate whether an +# instance/keypoint should be considered or not (expecting only either 0 or 1 +# value). In reality, the weights can be any value and this class should handle +# those values properly. +class CenterNetKeypointTargetAssigner(object): + """Wrapper to compute target tensors for the CenterNet keypoint estimation. + + This class has methods that take as input a batch of groundtruth tensors + (in the form of a list) and returns the targets required to train the + CenterNet model for keypoint estimation. Specifically, the class methods + expect the groundtruth in the following formats (consistent with the + standard Object Detection API). Note that usually the groundtruth tensors are + packed with a list which represents the batch dimension: + + gt_classes_list: [Required] a list of 2D tf.float32 one-hot + (or k-hot) tensors of shape [num_instances, num_classes] containing the + class targets with the 0th index assumed to map to the first non-background + class. + gt_keypoints_list: [Required] a list of 3D tf.float32 tensors of + shape [num_instances, num_total_keypoints, 2] containing keypoint + coordinates. Note that the "num_total_keypoints" should be the sum of the + num_keypoints over all possible keypoint types, e.g. human pose, face. + For example, if a dataset contains both 17 human pose keypoints and 5 face + keypoints, then num_total_keypoints = 17 + 5 = 22. + If an intance contains only a subet of keypoints (e.g. human pose keypoints + but not face keypoints), the face keypoints will be filled with zeros. + Also note that keypoints are assumed to be provided in normalized + coordinates and missing keypoints should be encoded as NaN. + gt_keypoints_weights_list: [Optional] a list 3D tf.float32 tensors of shape + [num_instances, num_total_keypoints] representing the weights of each + keypoints. If not provided, then all not NaN keypoints will be equally + weighted. + gt_boxes_list: [Optional] a list of 2D tf.float32 tensors of shape + [num_instances, 4] containing coordinates of the groundtruth boxes. + Groundtruth boxes are provided in [y_min, x_min, y_max, x_max] format and + assumed to be normalized and clipped relative to the image window with + y_min <= y_max and x_min <= x_max. + Note that the boxes are only used to compute the center targets but are not + considered as required output of the keypoint task. If the boxes were not + provided, the center targets will be inferred from the keypoints + [not implemented yet]. + gt_weights_list: [Optional] A list of 1D tf.float32 tensors of shape + [num_instances] containing weights for groundtruth boxes. Only useful when + gt_boxes_list is also provided. + """ + + def __init__(self, + stride, + class_id, + keypoint_indices, + keypoint_std_dev=None, + per_keypoint_offset=False, + peak_radius=0, + compute_heatmap_sparse=False): + """Initializes a CenterNet keypoints target assigner. + + Args: + stride: int, the stride of the network in output pixels. + class_id: int, the ID of the class (0-indexed) that contains the target + keypoints to consider in this task. For example, if the task is human + pose estimation, the class id should correspond to the "human" class. + keypoint_indices: A list of integers representing the indices of the + keypoints to be considered in this task. This is used to retrieve the + subset of the keypoints from gt_keypoints that should be considered in + this task. + keypoint_std_dev: A list of floats represent the standard deviation of the + Gaussian kernel used to generate the keypoint heatmap (in the unit of + output pixels). It is to provide the flexibility of using different + sizes of Gaussian kernel for each keypoint type. If not provided, then + all standard deviation will be the same as the default value (10.0 in + the output pixel space). If provided, the length of keypoint_std_dev + needs to be the same as the length of keypoint_indices, indicating the + standard deviation of each keypoint type. + per_keypoint_offset: boolean, indicating whether to assign offset for + each keypoint channel. If set False, the output offset target will have + the shape [batch_size, out_height, out_width, 2]. If set True, the + output offset target will have the shape [batch_size, out_height, + out_width, 2 * num_keypoints]. + peak_radius: int, the radius (in the unit of output pixel) around heatmap + peak to assign the offset targets. + compute_heatmap_sparse: bool, indicating whether or not to use the sparse + version of the Op that computes the heatmap. The sparse version scales + better with number of keypoint types, but in some cases is known to + cause an OOM error. See (b/170989061). + """ + + self._stride = stride + self._class_id = class_id + self._keypoint_indices = keypoint_indices + self._per_keypoint_offset = per_keypoint_offset + self._peak_radius = peak_radius + self._compute_heatmap_sparse = compute_heatmap_sparse + if keypoint_std_dev is None: + self._keypoint_std_dev = ([_DEFAULT_KEYPOINT_OFFSET_STD_DEV] * + len(keypoint_indices)) + else: + assert len(keypoint_indices) == len(keypoint_std_dev) + self._keypoint_std_dev = keypoint_std_dev + + def _preprocess_keypoints_and_weights(self, out_height, out_width, keypoints, + class_onehot, class_weights, + keypoint_weights): + """Preprocesses the keypoints and the corresponding keypoint weights. + + This function performs several common steps to preprocess the keypoints and + keypoint weights features, including: + 1) Select the subset of keypoints based on the keypoint indices, fill the + keypoint NaN values with zeros and convert to absoluate coordinates. + 2) Generate the weights of the keypoint using the following information: + a. The class of the instance. + b. The NaN value of the keypoint coordinates. + c. The provided keypoint weights. + + Args: + out_height: An integer or an interger tensor indicating the output height + of the model. + out_width: An integer or an interger tensor indicating the output width of + the model. + keypoints: A float tensor of shape [num_instances, num_total_keypoints, 2] + representing the original keypoint grountruth coordinates. + class_onehot: A float tensor of shape [num_instances, num_classes] + containing the class targets with the 0th index assumed to map to the + first non-background class. + class_weights: A float tensor of shape [num_instances] containing weights + for groundtruth instances. + keypoint_weights: A float tensor of shape + [num_instances, num_total_keypoints] representing the weights of each + keypoints. + + Returns: + A tuple of two tensors: + keypoint_absolute: A float tensor of shape + [num_instances, num_keypoints, 2] which is the selected and updated + keypoint coordinates. + keypoint_weights: A float tensor of shape [num_instances, num_keypoints] + representing the updated weight of each keypoint. + """ + # Select the targets keypoints by their type ids and generate the mask + # of valid elements. + valid_mask, keypoints = ta_utils.get_valid_keypoint_mask_for_class( + keypoint_coordinates=keypoints, + class_id=self._class_id, + class_onehot=class_onehot, + class_weights=class_weights, + keypoint_indices=self._keypoint_indices) + # Keypoint coordinates in absolute coordinate system. + # The shape of the tensors: [num_instances, num_keypoints, 2]. + keypoints_absolute = keypoint_ops.to_absolute_coordinates( + keypoints, out_height, out_width) + # Assign default weights for the keypoints. + if keypoint_weights is None: + keypoint_weights = tf.ones_like(keypoints[:, :, 0]) + else: + keypoint_weights = tf.gather( + keypoint_weights, indices=self._keypoint_indices, axis=1) + keypoint_weights = keypoint_weights * valid_mask + return keypoints_absolute, keypoint_weights + + def assign_keypoint_heatmap_targets(self, + height, + width, + gt_keypoints_list, + gt_classes_list, + gt_keypoints_weights_list=None, + gt_weights_list=None, + gt_boxes_list=None): + """Returns the keypoint heatmap targets for the CenterNet model. + + Args: + height: int, height of input to the CenterNet model. This is used to + determine the height of the output. + width: int, width of the input to the CenterNet model. This is used to + determine the width of the output. + gt_keypoints_list: A list of float tensors with shape [num_instances, + num_total_keypoints, 2]. See class-level description for more detail. + gt_classes_list: A list of float tensors with shape [num_instances, + num_classes]. See class-level description for more detail. + gt_keypoints_weights_list: A list of tensors with shape [num_instances, + num_total_keypoints] corresponding to the weight of each keypoint. + gt_weights_list: A list of float tensors with shape [num_instances]. See + class-level description for more detail. + gt_boxes_list: A list of float tensors with shape [num_instances, 4]. See + class-level description for more detail. If provided, the keypoint + standard deviations will be scaled based on the box sizes. + + Returns: + heatmap: A float tensor of shape [batch_size, output_height, output_width, + num_keypoints] representing the per keypoint type center heatmap. + output_height and output_width are computed by dividing the input height + and width by the stride specified during initialization. Note that the + "num_keypoints" is defined by the length of keypoint_indices, which is + not necessarily equal to "num_total_keypoints". + num_instances_batch: A 2D int tensor of shape + [batch_size, num_keypoints] representing number of instances for each + keypoint type. + valid_mask: A float tensor with shape [batch_size, output_height, + output_width] where all values within the regions of the blackout boxes + are 0.0 and 1.0 else where. + """ + out_width = tf.cast(width // self._stride, tf.float32) + out_height = tf.cast(height // self._stride, tf.float32) + # Compute the yx-grid to be used to generate the heatmap. Each returned + # tensor has shape of [out_height, out_width] + y_grid, x_grid = ta_utils.image_shape_to_grids(out_height, out_width) + + if gt_keypoints_weights_list is None: + gt_keypoints_weights_list = [None] * len(gt_keypoints_list) + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_classes_list) + if gt_boxes_list is None: + gt_boxes_list = [None] * len(gt_keypoints_list) + + heatmaps = [] + num_instances_list = [] + valid_mask_list = [] + for keypoints, classes, kp_weights, weights, boxes in zip( + gt_keypoints_list, gt_classes_list, gt_keypoints_weights_list, + gt_weights_list, gt_boxes_list): + keypoints_absolute, kp_weights = self._preprocess_keypoints_and_weights( + out_height=out_height, + out_width=out_width, + keypoints=keypoints, + class_onehot=classes, + class_weights=weights, + keypoint_weights=kp_weights) + num_instances, num_keypoints, _ = ( + shape_utils.combined_static_and_dynamic_shape(keypoints_absolute)) + + # A tensor of shape [num_instances, num_keypoints] with + # each element representing the type dimension for each corresponding + # keypoint: + # [[0, 1, ..., k-1], + # [0, 1, ..., k-1], + # : + # [0, 1, ..., k-1]] + keypoint_types = tf.tile( + input=tf.expand_dims(tf.range(num_keypoints), axis=0), + multiples=[num_instances, 1]) + + # A tensor of shape [num_instances, num_keypoints] with + # each element representing the sigma of the Gaussian kernel for each + # keypoint. + keypoint_std_dev = tf.tile( + input=tf.expand_dims(tf.constant(self._keypoint_std_dev), axis=0), + multiples=[num_instances, 1]) + + # If boxes is not None, then scale the standard deviation based on the + # size of the object bounding boxes similar to object center heatmap. + if boxes is not None: + boxes = box_list.BoxList(boxes) + # Convert the box coordinates to absolute output image dimension space. + boxes = box_list_ops.to_absolute_coordinates(boxes, + height // self._stride, + width // self._stride) + # Get the box height and width. Each returned tensors have the shape + # of [num_instances] + (_, _, boxes_height, + boxes_width) = boxes.get_center_coordinates_and_sizes() + + # Compute the sigma from box size. The tensor shape: [num_instances]. + sigma = _compute_std_dev_from_box_size(boxes_height, boxes_width, 0.7) + keypoint_std_dev = keypoint_std_dev * tf.stack( + [sigma] * num_keypoints, axis=1) + + # Generate the valid region mask to ignore regions with target class but + # no corresponding keypoints. + # Shape: [num_instances]. + blackout = tf.logical_and(classes[:, self._class_id] > 0, + tf.reduce_max(kp_weights, axis=1) < 1e-3) + valid_mask = ta_utils.blackout_pixel_weights_by_box_regions( + out_height, out_width, boxes.get(), blackout) + valid_mask_list.append(valid_mask) + + # Apply the Gaussian kernel to the keypoint coordinates. Returned heatmap + # has shape of [out_height, out_width, num_keypoints]. + heatmap = ta_utils.coordinates_to_heatmap( + y_grid=y_grid, + x_grid=x_grid, + y_coordinates=tf.keras.backend.flatten(keypoints_absolute[:, :, 0]), + x_coordinates=tf.keras.backend.flatten(keypoints_absolute[:, :, 1]), + sigma=tf.keras.backend.flatten(keypoint_std_dev), + channel_onehot=tf.one_hot( + tf.keras.backend.flatten(keypoint_types), depth=num_keypoints), + channel_weights=tf.keras.backend.flatten(kp_weights)) + num_instances_list.append( + tf.cast(tf.reduce_sum(kp_weights, axis=0), dtype=tf.int32)) + heatmaps.append(heatmap) + return (tf.stack(heatmaps, axis=0), tf.stack(num_instances_list, axis=0), + tf.stack(valid_mask_list, axis=0)) + + def _get_keypoint_types(self, num_instances, num_keypoints, num_neighbors): + """Gets keypoint type index tensor. + + The function prepares the tensor of keypoint indices with shape + [num_instances, num_keypoints, num_neighbors]. Each element represents the + keypoint type index for each corresponding keypoint and tiled along the 3rd + axis: + [[0, 1, ..., num_keypoints - 1], + [0, 1, ..., num_keypoints - 1], + : + [0, 1, ..., num_keypoints - 1]] + + Args: + num_instances: int, the number of instances, used to define the 1st + dimension. + num_keypoints: int, the number of keypoint types, used to define the 2nd + dimension. + num_neighbors: int, the number of neighborhood pixels to consider for each + keypoint, used to define the 3rd dimension. + + Returns: + A integer tensor of shape [num_instances, num_keypoints, num_neighbors]. + """ + keypoint_types = tf.range(num_keypoints)[tf.newaxis, :, tf.newaxis] + tiled_keypoint_types = tf.tile(keypoint_types, + multiples=[num_instances, 1, num_neighbors]) + return tiled_keypoint_types + + def assign_keypoints_offset_targets(self, + height, + width, + gt_keypoints_list, + gt_classes_list, + gt_keypoints_weights_list=None, + gt_weights_list=None): + """Returns the offsets and indices of the keypoints for location refinement. + + The returned values are used to refine the location of each keypoints in the + heatmap. The predicted values at the relevant indices can be retrieved with + the get_batch_predictions_from_indices function. + + Args: + height: int, height of input to the CenterNet model. This is used to + determine the height of the output. + width: int, width of the input to the CenterNet model. This is used to + determine the width of the output. + gt_keypoints_list: A list of tensors with shape [num_instances, + num_total_keypoints]. See class-level description for more detail. + gt_classes_list: A list of tensors with shape [num_instances, + num_classes]. See class-level description for more detail. + gt_keypoints_weights_list: A list of tensors with shape [num_instances, + num_total_keypoints] corresponding to the weight of each keypoint. + gt_weights_list: A list of float tensors with shape [num_instances]. See + class-level description for more detail. + + Returns: + batch_indices: an integer tensor of shape [num_total_instances, 3] (or + [num_total_instances, 4] if 'per_keypoint_offset' is set True) holding + the indices inside the predicted tensor which should be penalized. The + first column indicates the index along the batch dimension and the + second and third columns indicate the index along the y and x + dimensions respectively. The fourth column corresponds to the channel + dimension (if 'per_keypoint_offset' is set True). + batch_offsets: a float tensor of shape [num_total_instances, 2] holding + the expected y and x offset of each box in the output space. + batch_weights: a float tensor of shape [num_total_instances] indicating + the weight of each prediction. + Note that num_total_instances = batch_size * num_instances * + num_keypoints * num_neighbors + """ + + batch_indices = [] + batch_offsets = [] + batch_weights = [] + + if gt_keypoints_weights_list is None: + gt_keypoints_weights_list = [None] * len(gt_keypoints_list) + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_classes_list) + for i, (keypoints, classes, kp_weights, weights) in enumerate( + zip(gt_keypoints_list, gt_classes_list, gt_keypoints_weights_list, + gt_weights_list)): + keypoints_absolute, kp_weights = self._preprocess_keypoints_and_weights( + out_height=height // self._stride, + out_width=width // self._stride, + keypoints=keypoints, + class_onehot=classes, + class_weights=weights, + keypoint_weights=kp_weights) + num_instances, num_keypoints, _ = ( + shape_utils.combined_static_and_dynamic_shape(keypoints_absolute)) + + # [num_instances * num_keypoints] + y_source = tf.keras.backend.flatten(keypoints_absolute[:, :, 0]) + x_source = tf.keras.backend.flatten(keypoints_absolute[:, :, 1]) + + # All keypoint coordinates and their neighbors: + # [num_instance * num_keypoints, num_neighbors] + (y_source_neighbors, x_source_neighbors, + valid_sources) = ta_utils.get_surrounding_grids(height // self._stride, + width // self._stride, + y_source, x_source, + self._peak_radius) + _, num_neighbors = shape_utils.combined_static_and_dynamic_shape( + y_source_neighbors) + + # Update the valid keypoint weights. + # [num_instance * num_keypoints, num_neighbors] + valid_keypoints = tf.cast( + valid_sources, dtype=tf.float32) * tf.stack( + [tf.keras.backend.flatten(kp_weights)] * num_neighbors, axis=-1) + + # Compute the offsets and indices of the box centers. Shape: + # offsets: [num_instances * num_keypoints, num_neighbors, 2] + # indices: [num_instances * num_keypoints, num_neighbors, 2] + offsets, indices = ta_utils.compute_floor_offsets_with_indices( + y_source=y_source_neighbors, + x_source=x_source_neighbors, + y_target=y_source, + x_target=x_source) + # Reshape to: + # offsets: [num_instances * num_keypoints * num_neighbors, 2] + # indices: [num_instances * num_keypoints * num_neighbors, 2] + offsets = tf.reshape(offsets, [-1, 2]) + indices = tf.reshape(indices, [-1, 2]) + + # Prepare the batch indices to be prepended. + batch_index = tf.fill( + [num_instances * num_keypoints * num_neighbors, 1], i) + if self._per_keypoint_offset: + tiled_keypoint_types = self._get_keypoint_types( + num_instances, num_keypoints, num_neighbors) + batch_indices.append( + tf.concat([batch_index, indices, + tf.reshape(tiled_keypoint_types, [-1, 1])], axis=1)) + else: + batch_indices.append(tf.concat([batch_index, indices], axis=1)) + batch_offsets.append(offsets) + batch_weights.append(tf.keras.backend.flatten(valid_keypoints)) + + # Concatenate the tensors in the batch in the first dimension: + # shape: [batch_size * num_instances * num_keypoints * num_neighbors, 3] or + # [batch_size * num_instances * num_keypoints * num_neighbors, 4] if + # 'per_keypoint_offset' is set to True. + batch_indices = tf.concat(batch_indices, axis=0) + # shape: [batch_size * num_instances * num_keypoints * num_neighbors] + batch_weights = tf.concat(batch_weights, axis=0) + # shape: [batch_size * num_instances * num_keypoints * num_neighbors, 2] + batch_offsets = tf.concat(batch_offsets, axis=0) + return (batch_indices, batch_offsets, batch_weights) + + def assign_joint_regression_targets(self, + height, + width, + gt_keypoints_list, + gt_classes_list, + gt_boxes_list=None, + gt_keypoints_weights_list=None, + gt_weights_list=None): + """Returns the joint regression from center grid to keypoints. + + The joint regression is used as the grouping cue from the estimated + keypoints to instance center. The offsets are the vectors from the floored + object center coordinates to the keypoint coordinates. + + Args: + height: int, height of input to the CenterNet model. This is used to + determine the height of the output. + width: int, width of the input to the CenterNet model. This is used to + determine the width of the output. + gt_keypoints_list: A list of float tensors with shape [num_instances, + num_total_keypoints]. See class-level description for more detail. + gt_classes_list: A list of float tensors with shape [num_instances, + num_classes]. See class-level description for more detail. + gt_boxes_list: A list of float tensors with shape [num_instances, 4]. See + class-level description for more detail. If provided, then the center + targets will be computed based on the center of the boxes. + gt_keypoints_weights_list: A list of float tensors with shape + [num_instances, num_total_keypoints] representing to the weight of each + keypoint. + gt_weights_list: A list of float tensors with shape [num_instances]. See + class-level description for more detail. + + Returns: + batch_indices: an integer tensor of shape [num_instances, 4] holding the + indices inside the predicted tensor which should be penalized. The + first column indicates the index along the batch dimension and the + second and third columns indicate the index along the y and x + dimensions respectively, the last dimension refers to the keypoint type + dimension. + batch_offsets: a float tensor of shape [num_instances, 2] holding the + expected y and x offset of each box in the output space. + batch_weights: a float tensor of shape [num_instances] indicating the + weight of each prediction. + Note that num_total_instances = batch_size * num_instances * num_keypoints + + Raises: + NotImplementedError: currently the object center coordinates need to be + computed from groundtruth bounding boxes. The functionality of + generating the object center coordinates from keypoints is not + implemented yet. + """ + + batch_indices = [] + batch_offsets = [] + batch_weights = [] + batch_size = len(gt_keypoints_list) + if gt_keypoints_weights_list is None: + gt_keypoints_weights_list = [None] * batch_size + if gt_boxes_list is None: + gt_boxes_list = [None] * batch_size + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_classes_list) + for i, (keypoints, classes, boxes, kp_weights, weights) in enumerate( + zip(gt_keypoints_list, gt_classes_list, + gt_boxes_list, gt_keypoints_weights_list, gt_weights_list)): + keypoints_absolute, kp_weights = self._preprocess_keypoints_and_weights( + out_height=height // self._stride, + out_width=width // self._stride, + keypoints=keypoints, + class_onehot=classes, + class_weights=weights, + keypoint_weights=kp_weights) + num_instances, num_keypoints, _ = ( + shape_utils.combined_static_and_dynamic_shape(keypoints_absolute)) + + # If boxes are provided, compute the joint center from it. + if boxes is not None: + # Compute joint center from boxes. + boxes = box_list.BoxList(boxes) + boxes = box_list_ops.to_absolute_coordinates(boxes, + height // self._stride, + width // self._stride) + y_center, x_center, _, _ = boxes.get_center_coordinates_and_sizes() + else: + # TODO(yuhuic): Add the logic to generate object centers from keypoints. + raise NotImplementedError(( + 'The functionality of generating object centers from keypoints is' + ' not implemented yet. Please provide groundtruth bounding boxes.' + )) + + # Tile the yx center coordinates to be the same shape as keypoints. + y_center_tiled = tf.tile( + tf.reshape(y_center, shape=[num_instances, 1]), + multiples=[1, num_keypoints]) + x_center_tiled = tf.tile( + tf.reshape(x_center, shape=[num_instances, 1]), + multiples=[1, num_keypoints]) + # [num_instance * num_keypoints, num_neighbors] + (y_source_neighbors, x_source_neighbors, + valid_sources) = ta_utils.get_surrounding_grids( + height // self._stride, width // self._stride, + tf.keras.backend.flatten(y_center_tiled), + tf.keras.backend.flatten(x_center_tiled), self._peak_radius) + + _, num_neighbors = shape_utils.combined_static_and_dynamic_shape( + y_source_neighbors) + valid_keypoints = tf.cast( + valid_sources, dtype=tf.float32) * tf.stack( + [tf.keras.backend.flatten(kp_weights)] * num_neighbors, axis=-1) + + # Compute the offsets and indices of the box centers. Shape: + # offsets: [num_instances * num_keypoints, 2] + # indices: [num_instances * num_keypoints, 2] + (offsets, indices) = ta_utils.compute_floor_offsets_with_indices( + y_source=y_source_neighbors, + x_source=x_source_neighbors, + y_target=tf.keras.backend.flatten(keypoints_absolute[:, :, 0]), + x_target=tf.keras.backend.flatten(keypoints_absolute[:, :, 1])) + # Reshape to: + # offsets: [num_instances * num_keypoints * num_neighbors, 2] + # indices: [num_instances * num_keypoints * num_neighbors, 2] + offsets = tf.reshape(offsets, [-1, 2]) + indices = tf.reshape(indices, [-1, 2]) + + # keypoint type tensor: [num_instances, num_keypoints, num_neighbors]. + tiled_keypoint_types = self._get_keypoint_types( + num_instances, num_keypoints, num_neighbors) + + batch_index = tf.fill( + [num_instances * num_keypoints * num_neighbors, 1], i) + batch_indices.append( + tf.concat([batch_index, indices, + tf.reshape(tiled_keypoint_types, [-1, 1])], axis=1)) + batch_offsets.append(offsets) + batch_weights.append(tf.keras.backend.flatten(valid_keypoints)) + + # Concatenate the tensors in the batch in the first dimension: + # shape: [batch_size * num_instances * num_keypoints, 4] + batch_indices = tf.concat(batch_indices, axis=0) + # shape: [batch_size * num_instances * num_keypoints] + batch_weights = tf.concat(batch_weights, axis=0) + # shape: [batch_size * num_instances * num_keypoints, 2] + batch_offsets = tf.concat(batch_offsets, axis=0) + return (batch_indices, batch_offsets, batch_weights) + + +def _resize_masks(masks, height, width, method): + # Resize segmentation masks to conform to output dimensions. Use TF2 + # image resize because TF1's version is buggy: + # https://yaqs.corp.google.com/eng/q/4970450458378240 + masks = tf2.image.resize( + masks[:, :, :, tf.newaxis], + size=(height, width), + method=method) + return masks[:, :, :, 0] + + +class CenterNetMaskTargetAssigner(object): + """Wrapper to compute targets for segmentation masks.""" + + def __init__(self, stride): + self._stride = stride + + def assign_segmentation_targets( + self, gt_masks_list, gt_classes_list, + mask_resize_method=ResizeMethod.BILINEAR): + """Computes the segmentation targets. + + This utility produces a semantic segmentation mask for each class, starting + with whole image instance segmentation masks. Effectively, each per-class + segmentation target is the union of all masks from that class. + + Args: + gt_masks_list: A list of float tensors with shape [num_boxes, + input_height, input_width] with values in {0, 1} representing instance + masks for each object. + gt_classes_list: A list of float tensors with shape [num_boxes, + num_classes] representing the one-hot encoded class labels for each box + in the gt_boxes_list. + mask_resize_method: A `tf.compat.v2.image.ResizeMethod`. The method to use + when resizing masks from input resolution to output resolution. + + Returns: + segmentation_targets: An int32 tensor of size [batch_size, output_height, + output_width, num_classes] representing the class of each location in + the output space. + """ + # TODO(ronnyvotel): Handle groundtruth weights. + _, num_classes = shape_utils.combined_static_and_dynamic_shape( + gt_classes_list[0]) + + _, input_height, input_width = ( + shape_utils.combined_static_and_dynamic_shape(gt_masks_list[0])) + output_height = input_height // self._stride + output_width = input_width // self._stride + + segmentation_targets_list = [] + for gt_masks, gt_classes in zip(gt_masks_list, gt_classes_list): + gt_masks = _resize_masks(gt_masks, output_height, output_width, + mask_resize_method) + gt_masks = gt_masks[:, :, :, tf.newaxis] + gt_classes_reshaped = tf.reshape(gt_classes, [-1, 1, 1, num_classes]) + # Shape: [h, w, num_classes]. + segmentations_for_image = tf.reduce_max( + gt_masks * gt_classes_reshaped, axis=0) + # Avoid the case where max of an empty array is -inf. + segmentations_for_image = tf.maximum(segmentations_for_image, 0.0) + segmentation_targets_list.append(segmentations_for_image) + + segmentation_target = tf.stack(segmentation_targets_list, axis=0) + return segmentation_target + + +class CenterNetDensePoseTargetAssigner(object): + """Wrapper to compute targets for DensePose task.""" + + def __init__(self, stride, num_parts=24): + self._stride = stride + self._num_parts = num_parts + + def assign_part_and_coordinate_targets(self, + height, + width, + gt_dp_num_points_list, + gt_dp_part_ids_list, + gt_dp_surface_coords_list, + gt_weights_list=None): + """Returns the DensePose part_id and coordinate targets and their indices. + + The returned values are expected to be used with predicted tensors + of size (batch_size, height//self._stride, width//self._stride, 2). The + predicted values at the relevant indices can be retrieved with the + get_batch_predictions_from_indices function. + + Args: + height: int, height of input to the model. This is used to determine the + height of the output. + width: int, width of the input to the model. This is used to determine the + width of the output. + gt_dp_num_points_list: a list of 1-D tf.int32 tensors of shape [num_boxes] + containing the number of DensePose sampled points per box. + gt_dp_part_ids_list: a list of 2-D tf.int32 tensors of shape + [num_boxes, max_sampled_points] containing the DensePose part ids + (0-indexed) for each sampled point. Note that there may be padding, as + boxes may contain a different number of sampled points. + gt_dp_surface_coords_list: a list of 3-D tf.float32 tensors of shape + [num_boxes, max_sampled_points, 4] containing the DensePose surface + coordinates (normalized) for each sampled point. Note that there may be + padding. + gt_weights_list: A list of 1-D tensors with shape [num_boxes] + corresponding to the weight of each groundtruth detection box. + + Returns: + batch_indices: an integer tensor of shape [num_total_points, 4] holding + the indices inside the predicted tensor which should be penalized. The + first column indicates the index along the batch dimension and the + second and third columns indicate the index along the y and x + dimensions respectively. The fourth column is the part index. + batch_part_ids: an int tensor of shape [num_total_points, num_parts] + holding 1-hot encodings of parts for each sampled point. + batch_surface_coords: a float tensor of shape [num_total_points, 2] + holding the expected (v, u) coordinates for each sampled point. + batch_weights: a float tensor of shape [num_total_points] indicating the + weight of each prediction. + Note that num_total_points = batch_size * num_boxes * max_sampled_points. + """ + + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_dp_num_points_list) + + batch_indices = [] + batch_part_ids = [] + batch_surface_coords = [] + batch_weights = [] + + for i, (num_points, part_ids, surface_coords, weights) in enumerate( + zip(gt_dp_num_points_list, gt_dp_part_ids_list, + gt_dp_surface_coords_list, gt_weights_list)): + num_boxes, max_sampled_points = ( + shape_utils.combined_static_and_dynamic_shape(part_ids)) + part_ids_flattened = tf.reshape(part_ids, [-1]) + part_ids_one_hot = tf.one_hot(part_ids_flattened, depth=self._num_parts) + # Get DensePose coordinates in the output space. + surface_coords_abs = densepose_ops.to_absolute_coordinates( + surface_coords, height // self._stride, width // self._stride) + surface_coords_abs = tf.reshape(surface_coords_abs, [-1, 4]) + # Each tensor has shape [num_boxes * max_sampled_points]. + yabs, xabs, v, u = tf.unstack(surface_coords_abs, axis=-1) + + # Get the indices (in output space) for the DensePose coordinates. Note + # that if self._stride is larger than 1, this will have the effect of + # reducing spatial resolution of the groundtruth points. + indices_y = tf.cast(yabs, tf.int32) + indices_x = tf.cast(xabs, tf.int32) + + # Assign ones if weights are not provided. + if weights is None: + weights = tf.ones(num_boxes, dtype=tf.float32) + # Create per-point weights. + weights_per_point = tf.reshape( + tf.tile(weights[:, tf.newaxis], multiples=[1, max_sampled_points]), + shape=[-1]) + # Mask out invalid (i.e. padded) DensePose points. + num_points_tiled = tf.tile(num_points[:, tf.newaxis], + multiples=[1, max_sampled_points]) + range_tiled = tf.tile(tf.range(max_sampled_points)[tf.newaxis, :], + multiples=[num_boxes, 1]) + valid_points = tf.math.less(range_tiled, num_points_tiled) + valid_points = tf.cast(tf.reshape(valid_points, [-1]), dtype=tf.float32) + weights_per_point = weights_per_point * valid_points + + # Shape of [num_boxes * max_sampled_points] integer tensor filled with + # current batch index. + batch_index = i * tf.ones_like(indices_y, dtype=tf.int32) + batch_indices.append( + tf.stack([batch_index, indices_y, indices_x, part_ids_flattened], + axis=1)) + batch_part_ids.append(part_ids_one_hot) + batch_surface_coords.append(tf.stack([v, u], axis=1)) + batch_weights.append(weights_per_point) + + batch_indices = tf.concat(batch_indices, axis=0) + batch_part_ids = tf.concat(batch_part_ids, axis=0) + batch_surface_coords = tf.concat(batch_surface_coords, axis=0) + batch_weights = tf.concat(batch_weights, axis=0) + return batch_indices, batch_part_ids, batch_surface_coords, batch_weights + + +class CenterNetTrackTargetAssigner(object): + """Wrapper to compute targets for tracking task. + + Reference paper: A Simple Baseline for Multi-Object Tracking [1] + [1]: https://arxiv.org/abs/2004.01888 + """ + + def __init__(self, stride, num_track_ids): + self._stride = stride + self._num_track_ids = num_track_ids + + def assign_track_targets(self, + height, + width, + gt_track_ids_list, + gt_boxes_list, + gt_weights_list=None): + """Computes the track ID targets. + + Args: + height: int, height of input to the model. This is used to determine the + height of the output. + width: int, width of the input to the model. This is used to determine the + width of the output. + gt_track_ids_list: A list of 1-D tensors with shape [num_boxes] + corresponding to the track ID of each groundtruth detection box. + gt_boxes_list: A list of float tensors with shape [num_boxes, 4] + representing the groundtruth detection bounding boxes for each sample in + the batch. The coordinates are expected in normalized coordinates. + gt_weights_list: A list of 1-D tensors with shape [num_boxes] + corresponding to the weight of each groundtruth detection box. + + Returns: + batch_indices: an integer tensor of shape [batch_size, num_boxes, 3] + holding the indices inside the predicted tensor which should be + penalized. The first column indicates the index along the batch + dimension and the second and third columns indicate the index + along the y and x dimensions respectively. + batch_weights: a float tensor of shape [batch_size, num_boxes] indicating + the weight of each prediction. + track_id_targets: An int32 tensor of size [batch_size, num_boxes, + num_track_ids] containing the one-hot track ID vector of each + groundtruth detection box. + """ + track_id_targets = tf.one_hot( + gt_track_ids_list, depth=self._num_track_ids, axis=-1) + + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_boxes_list) + + batch_indices = [] + batch_weights = [] + + for i, (boxes, weights) in enumerate(zip(gt_boxes_list, gt_weights_list)): + boxes = box_list.BoxList(boxes) + boxes = box_list_ops.to_absolute_coordinates(boxes, + height // self._stride, + width // self._stride) + # Get the box center coordinates. Each returned tensors have the shape of + # [num_boxes] + (y_center, x_center, _, _) = boxes.get_center_coordinates_and_sizes() + num_boxes = tf.shape(x_center) + + # Compute the indices of the box centers. Shape: + # indices: [num_boxes, 2] + (_, indices) = ta_utils.compute_floor_offsets_with_indices( + y_source=y_center, x_source=x_center) + + # Assign ones if weights are not provided. + if weights is None: + weights = tf.ones(num_boxes, dtype=tf.float32) + + # Shape of [num_boxes, 1] integer tensor filled with current batch index. + batch_index = i * tf.ones_like(indices[:, 0:1], dtype=tf.int32) + batch_indices.append(tf.concat([batch_index, indices], axis=1)) + batch_weights.append(weights) + + batch_indices = tf.stack(batch_indices, axis=0) + batch_weights = tf.stack(batch_weights, axis=0) + + return batch_indices, batch_weights, track_id_targets + + +def filter_mask_overlap_min_area(masks): + """If a pixel belongs to 2 instances, remove it from the larger instance.""" + + num_instances = tf.shape(masks)[0] + def _filter_min_area(): + """Helper function to filter non empty masks.""" + areas = tf.reduce_sum(masks, axis=[1, 2], keepdims=True) + per_pixel_area = masks * areas + # Make sure background is ignored in argmin. + per_pixel_area = (masks * per_pixel_area + + (1 - masks) * per_pixel_area.dtype.max) + min_index = tf.cast(tf.argmin(per_pixel_area, axis=0), tf.int32) + + filtered_masks = ( + tf.range(num_instances)[:, tf.newaxis, tf.newaxis] + == + min_index[tf.newaxis, :, :] + ) + + return tf.cast(filtered_masks, tf.float32) * masks + + return tf.cond(num_instances > 0, _filter_min_area, + lambda: masks) + + +def filter_mask_overlap(masks, method='min_area'): + + if method == 'min_area': + return filter_mask_overlap_min_area(masks) + else: + raise ValueError('Unknown mask overlap filter type - {}'.format(method)) + + +class CenterNetCornerOffsetTargetAssigner(object): + """Wrapper to compute corner offsets for boxes using masks.""" + + def __init__(self, stride, overlap_resolution='min_area'): + """Initializes the corner offset target assigner. + + Args: + stride: int, the stride of the network in output pixels. + overlap_resolution: string, specifies how we handle overlapping + instance masks. Currently only 'min_area' is supported which assigns + overlapping pixels to the instance with the minimum area. + """ + + self._stride = stride + self._overlap_resolution = overlap_resolution + + def assign_corner_offset_targets( + self, gt_boxes_list, gt_masks_list): + """Computes the corner offset targets and foreground map. + + For each pixel that is part of any object's foreground, this function + computes the relative offsets to the top-left and bottom-right corners of + that instance's bounding box. It also returns a foreground map to indicate + which pixels contain valid corner offsets. + + Args: + gt_boxes_list: A list of float tensors with shape [num_boxes, 4] + representing the groundtruth detection bounding boxes for each sample in + the batch. The coordinates are expected in normalized coordinates. + gt_masks_list: A list of float tensors with shape [num_boxes, + input_height, input_width] with values in {0, 1} representing instance + masks for each object. + + Returns: + corner_offsets: A float tensor of shape [batch_size, height, width, 4] + containing, in order, the (y, x) offsets to the top left corner and + the (y, x) offsets to the bottom right corner for each foregroung pixel + foreground: A float tensor of shape [batch_size, height, width] in which + each pixel is set to 1 if it is a part of any instance's foreground + (and thus contains valid corner offsets) and 0 otherwise. + + """ + _, input_height, input_width = ( + shape_utils.combined_static_and_dynamic_shape(gt_masks_list[0])) + output_height = input_height // self._stride + output_width = input_width // self._stride + y_grid, x_grid = tf.meshgrid( + tf.range(output_height), tf.range(output_width), + indexing='ij') + y_grid, x_grid = tf.cast(y_grid, tf.float32), tf.cast(x_grid, tf.float32) + + corner_targets = [] + foreground_targets = [] + for gt_masks, gt_boxes in zip(gt_masks_list, gt_boxes_list): + gt_masks = _resize_masks(gt_masks, output_height, output_width, + method=ResizeMethod.NEAREST_NEIGHBOR) + gt_masks = filter_mask_overlap(gt_masks, self._overlap_resolution) + + ymin, xmin, ymax, xmax = tf.unstack(gt_boxes, axis=1) + ymin, ymax = ymin * output_height, ymax * output_height + xmin, xmax = xmin * output_width, xmax * output_width + + top_y = ymin[:, tf.newaxis, tf.newaxis] - y_grid[tf.newaxis] + left_x = xmin[:, tf.newaxis, tf.newaxis] - x_grid[tf.newaxis] + bottom_y = ymax[:, tf.newaxis, tf.newaxis] - y_grid[tf.newaxis] + right_x = xmax[:, tf.newaxis, tf.newaxis] - x_grid[tf.newaxis] + + foreground_target = tf.cast(tf.reduce_sum(gt_masks, axis=0) > 0.5, + tf.float32) + foreground_targets.append(foreground_target) + + corner_target = tf.stack([ + tf.reduce_sum(top_y * gt_masks, axis=0), + tf.reduce_sum(left_x * gt_masks, axis=0), + tf.reduce_sum(bottom_y * gt_masks, axis=0), + tf.reduce_sum(right_x * gt_masks, axis=0), + ], axis=2) + + corner_targets.append(corner_target) + + return (tf.stack(corner_targets, axis=0), + tf.stack(foreground_targets, axis=0)) + + +class CenterNetTemporalOffsetTargetAssigner(object): + """Wrapper to compute target tensors for the temporal offset task. + + This class has methods that take as input a batch of ground truth tensors + (in the form of a list) and returns the targets required to train the + temporal offset task. + """ + + def __init__(self, stride): + """Initializes the target assigner. + + Args: + stride: int, the stride of the network in output pixels. + """ + + self._stride = stride + + def assign_temporal_offset_targets(self, + height, + width, + gt_boxes_list, + gt_offsets_list, + gt_match_list, + gt_weights_list=None): + """Returns the temporal offset targets and their indices. + + For each ground truth box, this function assigns it the corresponding + temporal offset to train the model. + + Args: + height: int, height of input to the model. This is used to determine the + height of the output. + width: int, width of the input to the model. This is used to determine the + width of the output. + gt_boxes_list: A list of float tensors with shape [num_boxes, 4] + representing the groundtruth detection bounding boxes for each sample in + the batch. The coordinates are expected in normalized coordinates. + gt_offsets_list: A list of 2-D tf.float32 tensors of shape [num_boxes, 2] + containing the spatial offsets of objects' centers compared with the + previous frame. + gt_match_list: A list of 1-D tf.float32 tensors of shape [num_boxes] + containing flags that indicate if an object has existed in the + previous frame. + gt_weights_list: A list of tensors with shape [num_boxes] corresponding to + the weight of each groundtruth detection box. + + Returns: + batch_indices: an integer tensor of shape [num_boxes, 3] holding the + indices inside the predicted tensor which should be penalized. The + first column indicates the index along the batch dimension and the + second and third columns indicate the index along the y and x + dimensions respectively. + batch_temporal_offsets: a float tensor of shape [num_boxes, 2] of the + expected y and x temporal offset of each object center in the + output space. + batch_weights: a float tensor of shape [num_boxes] indicating the + weight of each prediction. + """ + + if gt_weights_list is None: + gt_weights_list = [None] * len(gt_boxes_list) + + batch_indices = [] + batch_weights = [] + batch_temporal_offsets = [] + + for i, (boxes, offsets, match_flags, weights) in enumerate(zip( + gt_boxes_list, gt_offsets_list, gt_match_list, gt_weights_list)): + boxes = box_list.BoxList(boxes) + boxes = box_list_ops.to_absolute_coordinates(boxes, + height // self._stride, + width // self._stride) + # Get the box center coordinates. Each returned tensors have the shape of + # [num_boxes] + (y_center, x_center, _, _) = boxes.get_center_coordinates_and_sizes() + num_boxes = tf.shape(x_center) + + # Compute the offsets and indices of the box centers. Shape: + # offsets: [num_boxes, 2] + # indices: [num_boxes, 2] + (_, indices) = ta_utils.compute_floor_offsets_with_indices( + y_source=y_center, x_source=x_center) + + # Assign ones if weights are not provided. + # if an object is not matched, its weight becomes zero. + if weights is None: + weights = tf.ones(num_boxes, dtype=tf.float32) + weights *= match_flags + + # Shape of [num_boxes, 1] integer tensor filled with current batch index. + batch_index = i * tf.ones_like(indices[:, 0:1], dtype=tf.int32) + batch_indices.append(tf.concat([batch_index, indices], axis=1)) + batch_weights.append(weights) + batch_temporal_offsets.append(offsets) + + batch_indices = tf.concat(batch_indices, axis=0) + batch_weights = tf.concat(batch_weights, axis=0) + batch_temporal_offsets = tf.concat(batch_temporal_offsets, axis=0) + return (batch_indices, batch_temporal_offsets, batch_weights) + + +class DETRTargetAssigner(object): + """Target assigner for DETR (https://arxiv.org/abs/2005.12872). + + Detection Transformer (DETR) matches predicted boxes to groundtruth directly + to determine targets instead of matching anchors to groundtruth. Hence, the + new target assigner. + """ + + def __init__(self): + """Construct Object Detection Target Assigner.""" + self._similarity_calc = sim_calc.DETRSimilarity() + self._matcher = hungarian_matcher.HungarianBipartiteMatcher() + + def batch_assign(self, + pred_box_batch, + gt_box_batch, + pred_class_batch, + gt_class_targets_batch, + gt_weights_batch=None, + unmatched_class_label_batch=None): + """Batched assignment of classification and regression targets. + + Args: + pred_box_batch: a tensor of shape [batch_size, num_queries, 4] + representing predicted bounding boxes. + gt_box_batch: a tensor of shape [batch_size, num_queries, 4] + representing groundtruth bounding boxes. + pred_class_batch: A list of tensors with length batch_size, where each + each tensor has shape [num_queries, num_classes] to be used + by certain similarity calculators. + gt_class_targets_batch: a list of tensors with length batch_size, where + each tensor has shape [num_gt_boxes_i, num_classes] and + num_gt_boxes_i is the number of boxes in the ith boxlist of + gt_box_batch. + gt_weights_batch: A list of 1-D tf.float32 tensors of shape + [num_boxes] containing weights for groundtruth boxes. + unmatched_class_label_batch: a float32 tensor with shape + [d_1, d_2, ..., d_k] which is consistent with the classification target + for each anchor (and can be empty for scalar targets). This shape must + thus be compatible with the `gt_class_targets_batch`. + + Returns: + batch_cls_targets: a tensor with shape [batch_size, num_pred_boxes, + num_classes], + batch_cls_weights: a tensor with shape [batch_size, num_pred_boxes, + num_classes], + batch_reg_targets: a tensor with shape [batch_size, num_pred_boxes, + box_code_dimension] + batch_reg_weights: a tensor with shape [batch_size, num_pred_boxes]. + """ + pred_box_batch = [ + box_list.BoxList(pred_box) + for pred_box in tf.unstack(pred_box_batch)] + gt_box_batch = [ + box_list.BoxList(gt_box) + for gt_box in tf.unstack(gt_box_batch)] + + cls_targets_list = [] + cls_weights_list = [] + reg_targets_list = [] + reg_weights_list = [] + if gt_weights_batch is None: + gt_weights_batch = [None] * len(gt_class_targets_batch) + if unmatched_class_label_batch is None: + unmatched_class_label_batch = [None] * len(gt_class_targets_batch) + pred_class_batch = tf.unstack(pred_class_batch) + for (pred_boxes, gt_boxes, pred_class_batch, gt_class_targets, gt_weights, + unmatched_class_label) in zip(pred_box_batch, gt_box_batch, + pred_class_batch, gt_class_targets_batch, + gt_weights_batch, + unmatched_class_label_batch): + (cls_targets, cls_weights, reg_targets, + reg_weights) = self.assign(pred_boxes, gt_boxes, pred_class_batch, + gt_class_targets, gt_weights, + unmatched_class_label) + cls_targets_list.append(cls_targets) + cls_weights_list.append(cls_weights) + reg_targets_list.append(reg_targets) + reg_weights_list.append(reg_weights) + batch_cls_targets = tf.stack(cls_targets_list) + batch_cls_weights = tf.stack(cls_weights_list) + batch_reg_targets = tf.stack(reg_targets_list) + batch_reg_weights = tf.stack(reg_weights_list) + return (batch_cls_targets, batch_cls_weights, batch_reg_targets, + batch_reg_weights) + + def assign(self, + pred_boxes, + gt_boxes, + pred_classes, + gt_labels, + gt_weights=None, + unmatched_class_label=None): + """Assign classification and regression targets to each box_pred. + + For a given set of pred_boxes and groundtruth detections, match pred_boxes + to gt_boxes and assign classification and regression targets to + each box_pred as well as weights based on the resulting match (specifying, + e.g., which pred_boxes should not contribute to training loss). + + pred_boxes that are not matched to anything are given a classification + target of `unmatched_cls_target`. + + Args: + pred_boxes: a BoxList representing N pred_boxes + gt_boxes: a BoxList representing M groundtruth boxes + pred_classes: A tensor with shape [max_num_boxes, num_classes] + to be used by certain similarity calculators. + gt_labels: a tensor of shape [M, num_classes] + with labels for each of the ground_truth boxes. The subshape + [num_classes] can be empty (corresponding to scalar inputs). When set + to None, gt_labels assumes a binary problem where all + ground_truth boxes get a positive label (of 1). + gt_weights: a float tensor of shape [M] indicating the weight to + assign to all pred_boxes match to a particular groundtruth box. The + weights must be in [0., 1.]. If None, all weights are set to 1. + Generally no groundtruth boxes with zero weight match to any pred_boxes + as matchers are aware of groundtruth weights. Additionally, + `cls_weights` and `reg_weights` are calculated using groundtruth + weights as an added safety. + unmatched_class_label: a float32 tensor with shape [d_1, d_2, ..., d_k] + which is consistent with the classification target for each + anchor (and can be empty for scalar targets). This shape must thus be + compatible with the groundtruth labels that are passed to the "assign" + function (which have shape [num_gt_boxes, d_1, d_2, ..., d_k]). + + Returns: + cls_targets: a float32 tensor with shape [num_pred_boxes, num_classes], + where the subshape [num_classes] is compatible with gt_labels + which has shape [num_gt_boxes, num_classes]. + cls_weights: a float32 tensor with shape [num_pred_boxes, num_classes], + representing weights for each element in cls_targets. + reg_targets: a float32 tensor with shape [num_pred_boxes, + box_code_dimension] + reg_weights: a float32 tensor with shape [num_pred_boxes] + + """ + if not unmatched_class_label: + unmatched_class_label = tf.constant( + [1] + [0] * (gt_labels.shape[1] - 1), tf.float32) + + if gt_weights is None: + num_gt_boxes = gt_boxes.num_boxes_static() + if not num_gt_boxes: + num_gt_boxes = gt_boxes.num_boxes() + gt_weights = tf.ones([num_gt_boxes], dtype=tf.float32) + + gt_boxes.add_field(fields.BoxListFields.classes, gt_labels) + pred_boxes.add_field(fields.BoxListFields.classes, pred_classes) + + match_quality_matrix = self._similarity_calc.compare( + gt_boxes, + pred_boxes) + match = self._matcher.match(match_quality_matrix, + valid_rows=tf.greater(gt_weights, 0)) + + matched_gt_boxes = match.gather_based_on_match( + gt_boxes.get(), + unmatched_value=tf.zeros(4), + ignored_value=tf.zeros(4)) + matched_gt_boxlist = box_list.BoxList(matched_gt_boxes) + ty, tx, th, tw = matched_gt_boxlist.get_center_coordinates_and_sizes() + reg_targets = tf.transpose(tf.stack([ty, tx, th, tw])) + cls_targets = match.gather_based_on_match( + gt_labels, + unmatched_value=unmatched_class_label, + ignored_value=unmatched_class_label) + reg_weights = match.gather_based_on_match( + gt_weights, + ignored_value=0., + unmatched_value=0.) + cls_weights = match.gather_based_on_match( + gt_weights, + ignored_value=0., + unmatched_value=1) + + # convert cls_weights from per-box_pred to per-class. + class_label_shape = tf.shape(cls_targets)[1:] + weights_multiple = tf.concat( + [tf.constant([1]), class_label_shape], + axis=0) + cls_weights = tf.expand_dims(cls_weights, -1) + cls_weights = tf.tile(cls_weights, weights_multiple) + + return (cls_targets, cls_weights, reg_targets, reg_weights) diff --git a/workspace/virtuallab/object_detection/core/target_assigner.pyc b/workspace/virtuallab/object_detection/core/target_assigner.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f903a36622531607d30bd4349d665567c51456d GIT binary patch literal 84528 zcmeIb3y@vcn%B2ax72#tvSq#e-jZLfYq|BX?@MzZmMq)XbFXF3k?y@Svc>UHpVO@) zb@yqXecEc>v49$z0cI#5aA7ht5CX%qC{mRGQwf12RTNNEC6Hull1fp)P!JvonLvPq zB!tQD|9xw%z4z(UEy-$64PZ%UYwxw!Uhl{E{lCYW{Ac&={EvV8&$MQ<@MkOkzx-Y! zD<&qhtdnJzxHhuRM7Ge#7ALYx6InygllDBBU7GagE%v-6yR^lhw`L1lv&E_G(v&|> z+4HvS(l&qImMv`0uI3kNfl9Y~hJ)@yYDclm7gWJ?Gh_+@JSl3x~7CBiW@R{=DCw zpUN&h?av47`I+p}Gg+hiOm^+r?4v9j{8nqQ#!{Ab@6WDHXV;EqmyTu~ zO8B{~^KjOEKAWQ}9?3q=^yQCSI+k@`(Ch9@)_FAhnBF=&*hwZ_9cbK<}qBpm-2!X6mMzvYJX7V(^vW{OP$`*e14_B+AWUqqO)^;pupi^Wq393bcfy9VXwcm zHtw-}*bk-2XBSkgx!!DBag|Cw=*|zi1ooG9ddUmL@3v>JhTQo!ztLS-@c(=BSBGn2 zOc%@D+1}hu3V1B<&dkpo+j-+^FQriA#nt}GLMLD95A)gn(s0nbvNEKl`uT9s?k%Za z3;m)vN+Z1yTdo)>?D4Xop^1z9%FTSaJD}YbmBf(to$D_w^l#{?7qTd&0py+ zF0Tv~w%qOwZuID*qPN&vXb*bBoB5UQ@J5#wk4>#MskW>&c)n8T`}WQ}Umo;7=ykfC zyxE|>}-Gam41iRUaF=xG9=;{sFk#qVzqG=+E=;@#m=4Q z`|8fC?cvC{E~x9m&Ad-9wTVH+D+6_I&|jo{YT1SE&fa2szMGRmJ0JEIyT|f^QM+`L zfid5k^|`UUyr3aQjFH*HKw4Z`81|N9It9}emH zZg3gy;nKcR^cNV9t={5te=zhH+j-IHebCdy@R0inSswJ3hON1kC7Wv=uHA6ZUYhUv z<0OyY>n)%6r~N$5wHb?p)?jvNsl{k&&8id1*NffuQmYttI^7ROU+>`SkuOv(1ugW7 zP}1F%kXC=W@RaW(w9{QGy375dTLvrg_3q8(zUq-8ZzI(DIF{=%0vKl0y;Y9V)@&N! zo`)K*7}CJ)L8mp>>n?Oc=4vX|x5f5qtCsA8gkN1*nrG%w=h~}XO6zKSx!Yo@E`)T{ zdOjdqaXGEN-l^Aftq;0`vX_*_mELlDFznHzA&Yqe`SVY{K2*u-gJ;%WaoNJ}tf-pr zanoh%W8dUD5ojCj7?W9di|88dW;|}qI=kb)Ew-0#%Q{>2_R{vOGi5hBv_&XnXV%$n zH;^(r?1pc5+Rc4gXP4dFpEWN`tLKU{T)dp+M#3ncwWX{(vNVpY<8gVMN!6a!7}ZaM z^A*+cC%GAB*@um6*vPI;WFJjr#o=r?smfBdA8TY#6pApB4Yy?1wpxU<7JCJF*Wa?Ph28;TF50HMX#Ys&EB`$X;O!V@sPI=5JlO z#+H_!i#uDG^kE_F4%jSm{+t=iv&`!*udJvxR!_51jIPp^McTg!Lxi|XdEc^Zr8R11 zwm<03yy=TDt266}mKr_R_K0CW6d;e=$V>=Gv$xfwUhFa54Kg|#7U+~ycS&nSO4~Bl z0$5#w$)U>z-Gc~VA7>vP+J5hZLoCKpcb*;pgKo=K`__%Fw(--W!^ng6ISVHfu0Pvj z)wVjv1X5Yzb~)YY&b3*4Pv<9RP8^-dMdXBZ;u73j%HKILbBqX=L!ULApG`^arQ&-rQ&>=Dot`hK;&1XUp~8%#}ju((=pLgAiD}PJL2Fh%Ila6fbEorhVOp zQrb^8Dn{`zAI8jM?b~_Cz0jgs=3TyznV+B5>O9n{#Mjpbz4JsEsI_#;g~VxVtcCo9jqB zHm!<#iIG|&&5WUz`|;`WBAeEw5UqXuYJahN98&r?v^Dg{@zHsET=Vw$$j&jdeABB% zhP`2{^_r5~r$qPdZR}~p-;Rd<&WbvSKU*=?a4cchGFB_S@D0BCB)9h5Jghr@n^92f zCiWm$Y^69cJH=JaqrqXtd4bNo(*-=O@M{)u7Gbuh>)W$IFB@*nu1#ron`cwt-j?)R zTeK0dXoqNj(q3)1D~tJrnDdXIG+6U(U%16~z_+%}v#=9Lh_;el*|ptvy-%sJgRoIP zsZgGY$F4)vsqyjh5RcsMS8o1wt}nJ_AF>O372a;BJbM)57CX-NtT<&MRzgBl49aG& zs3RM6sAi*aYpdQ;B(Et6{wDVQ$!u;Sd-%{Tujm8W@WJfb-t5{#*@rt++=f2imwh;u zeYh=~x9b0n#~Td|lKt7W1GeXRz1ZytJeXZOlznJ)fWCa#iXIxSach^wc|=>XHp^Yv zN0ZsD-4^($?V9YD9!QIk+GBd8@@j?qE#&b^$R4ZP6WNFN#kRgh`N@?aTBwoTde9!9 zv}>My_@Dwmd_eVpJW+qrDtoilFJwdLf+I@k+EZ#;^_#k@k)6AID7*ELB|r=9(MGPq z?8)B$jT7HhUQa9Z!@VK&yR_;L{V>%FT|~?6v##5b4GvDKO3!2;QsI3T-)l|?we=9M zHX+p3L%iCAkc)@xvC&FQLP&uKjId|3xvflEO`YNwp);M68r}TFEREr;*bFPYaM-_I zTc1-`GS%%l(GoEDwlzcw!L}MwuSC`w%U-S|oZP+RGFoDlayqYG=h1w6fKVt3OWl72kGKyM2~Df=(Ua226S+$vOkV&477~MMRvt+!C5g9mT98yk~Pu! zTA$@>yKLPr->k+J;n6LuGW9f^i#`qUm=9su7+k8Xn4$cgH;?6=*2!b}%*+fo*Dr_9 zbEo+HOXH5M_xVo9f{&?|LqnFU6K2KA6-zcH@=h)7JhWeTaT#D_noU#`W#54(hH3!= z*oK5!0!{&}$@A}Cb*L((N2K%pC7Ac>s1Ai>NUtnvc;{DoOYH&B&7gl}p}R=mbr}J$ z<|&!A<!+XEl6KRU7W*&(t`p${VsjnntJ?f1HaeymGbQ~{^q%1X1w z(x^YfN^>`*VGbBz+3EnwvJCc@;ucgHAa|Z%{Mbe=pDj_^%Fq>WDHkmzB{bU zAwKG9ob>@-7uw5OS6Letlp1?2$$P2dgx?73iE9~~VWnjv9n7Mmd>ItOo6F?_ry(i$ zd*-YYYu0Inpplqd!d8|dL03G@>$O>|jcBrg-mmbdCoUg029`(JpEnrfgZ4tN(;D<| z6b5s&S9`_ub3|zh6ln^pF|;L=tSLaqB5G$CikBbG83*Z#5s6p8?t(zC zF4Gg5cEwO~08I#L-YuGfxQBvJw7Cz4E!(hL-S=183-D@L#@Ysj)$JfufL54g)g+ml zOYKGOEv^AW5L5>Jq;x}c%Zvrw0e!Gt1Q-VG4+TtzioNC`dRYi-DTV@4sMK)KUjRQ{ z?k*vGV3-um$9ZA3v?X`YZ9bs@HLFbwdxZoNpmAXER%_NCS{g(x>0q_qKORho-|;k%d>qxwOt zrG{(yg^wT~PQ%A|<>}hUVa$2a3&~*Uq@f_Z>K@qq5&<>t6h``Jk5=DYYIOq$@qOz-O7F1`$gqC4t z1slwYWiHw2fF@4tZam6we}mt|RO10q#wq^p!25 z5zklj&K|&V8a!N>*5Xt=O;cUyO29Ar$(E164`>nA)@f=BVQH_5Ui`)BN~EyO&g5^9 z-dt}0SDk1GHbXWHHZkoXH;}`#CC5#y(np>g0awtYpafxZL|lTsE6QY)&H%A|CI9D2 z5^f~-xHHvB(KC@QF#gaQz*z9!(p;bNFGz~q3#GJOVp^@;e3gsOT&)g;uN9j1HmRo7 zD?YomSb8fpFX`5haBI#GmB3BGj$bF8f5@-Uc9iYf%W%7|F{^@T@ll`+k(cOeMgv1c zu&E6Zgnw*O*BBy(W`Y7%A3thL00*;6Ar5rR7T8qZ0;Fyet_tkSa2ot&3*1=?1lkRG z0Wrs1Ku)g#X|lm?F-$S#H~K<38`3KbIc5duDiBuiG}s~VBxoPyal{pbdQW!k0n5~& zO+Z}LWv@kkNSOlSf@MBT-okDHEjhpW zZ?6RoORC(*?3fu72D51Au&aJ5sFM>7fv~j&S~=PcGSoLsmg(}7u?$*e(Y_6pfBu-* z*RjlkNrKAl1|n*u+q11wBQnsWEf3aW?bAzplx=OuE`f+z24J>ONwC56(F3j(&lXxj!os}=M_~_9b4F40&65!^OW9v zLzma|+DMHc{aj0}3=a{4LlcwvlRUeYo)$VLRC>c{@49lQvatq_Y}X z`c3k)74Ru;kfE{mX$A3xuOac*K8e5xGJ!_e8kZ{*UYkSc`LH9}d=C{DLqyw%dgV6E z{BtfQk1yb(qnbt)BQnrudWg2%{8cs^o3()CMUHBcbN8;ecS8UlrAX?AHKfV4?d)Um zPxK4KwV)Z5JCsZXa6-v9Rutj-s{yLAFUuO(XCu|6NGhu|_QWHOpgM6@M516r2o@f9 zy>-La0ZkU0=*?rgv$?71E!_wN*Wc?lU*PFe5W-$1*(d0B7sBqcEmW(PHUJwyV(-GX zhCI;(ckG;OiELOGj65j*C?C9H9L;#h7}5ylw<5ZUJB3k{tkiWzCk{7=p^@)2>eRnB z3xO!CUn+}2fXodVruUK>BRZ7!32^?I4J0;}oWRnxQXA#!Jh4>*h&Ptole5bbWcUFzlIhn9E7l0Fs}irKQ=Kcb3y~t=Y(cL^Gz%djGGF==N#IpP6bi0(#nam;Vh*bPj`ea9Hk$^AbUGX*r#oXbvv>( zaU&E>4S=qw6>2a^Wt7{C5_mxh}yath5GHzH4F}1HsdA- zPPjq1UqNX?!j`bVL3_0q7W<%K?S&ido4$64gqp>MLlP=y#!EwqNMl!Nr_ZK;O^W;p zr+=Naw()^pjgOl37Xr4Ktf|ehnR9Uv@UR{?e>HfSWZzAjUfl9ZmZSl&$e34XJTZiJ&cZqH%*ha`8k4{@8}|Mv?&~+DekT* z@}sFmxB0RzF#VOa+*b8_(>QJlnt6qv$tUT+sojt6***y%vHRwZ2X;KWvul^2CmLJ5Jp2Ad5aOkfKjOrTnH zFsJN(Yk*A^K9wyUm69sJr&9Zim6ZRY#9Bge{qvoIXTeC(ltp(F^A$8hfvvGxf$9Ay z+XDPc-+{Ygz7i~9?#nv&W%DXPLmB;7Rt3-KS;-kX_gkjVHpXX!5-Dn`WfqgS&*HLW z@t`GpWBp_)|6VJ3jLzJ00$CnX>R$fj$2)AOT7oPkhl%kjY4c;MNlK$ z7MWbj;oQ?=eyG_%`zmk6l*tA%NbRDDs%x$KC(hr5bz0O2?3<9SVg*~TjuA8$-~P6@ zZEaDjH`;01q3B5qBA^zrSFBU2wkhAKTc)^R-4VFhKi%i8nWab3TLQuEb=0)BUcD&| zTh=sth@DUfMSgwI?RIXSjb>_Z1_L9~5MEcTEXxkj#7+6d*YZ;{KRjb=-fLz^*nH*u zd6S145rCL~conEo$}017_UG?^-GJOgW>q4*Xn%g;>xND(0IeyZL4 z&-}Fh{AXV;1o)lr=hm)h0ZFgHo1dAGdcep_>xG%-(|j;3(7P#&+aUZN8i;eJOckSf zP=R`LzB_;6{d0)xE+jjj=03fY&|!S{G{uY>0{~icFNbeEO70+l@He^+S!su_gjiSl3KenjnShD$7{Gf!C zh7H6`kx%VaN@L3;dtbk>L~ZxvacLr(XcWKMKCbMj3}d!3U9s}uy4|ij?8+X(_Q?K* z1gds`=SX4LQaf2uw1}| zfn5h{m#x{mTV+#$`35E%SY%*vA@L|eNwec@*1`CQRFufrw6HHCD#haE zVD{l5x3{ATCyg0sDlZft-zyzBDmBp7Nfmh z7Ww$Z!!}pLE7m}gB@^%F^FhJUif9Q?1_z=;lddg|D?MPMlx?DAoNm5q1wPs(3E@L zX29>R`Afm1t;e9adjo(lCN@X?-xTOJOZh+1tM>rA)Bb^5j>GCq8ssNmIF}FSX6mLF zxPu5dVdz2ArM8Z)Dfvd^Mzr*rG4E7bRd>f{WJR6KmIk+^vB?#n$#Ui%(>P1&A{eS@EJm+$T$zmK&>Y;T2W#p(XDw`A~bKFZps^9qo16v(}Za z2-ZUo*dBr%) z1IWnbgBiqSB0t?d#&;U;Lu!`R-;=~GwZ2X1$aE+n|FUf6gsJmv77boV-Qg?qSP!i( z55D$KM0P8esmxpz)GM7-+GAD6P&dU)(c*h+RT}7f!ktHVm)OT?&W5(ew$G6||Jo#_ zV~LEJ^V0&etc{HA(dOMeN08}GV=>@7nPGEV6jK15v25*SDUdglau=M=?Dc6=rZ=|A zP|U=J81q$Y1k%t9lWCeJJnw7UnFUsM6<6<+URGsCC<802c|9;Ds`?ur zZwsEqu~A~Yua_1@dZj;FQv0ebUh{%l>)X0Wy{dUp7opF_x%*tHlzSvJ6sHneDfvif zjH{0Hbgk^0HLk6nJYC0N{(^?~Su)E`>_N|YvLU_a9gPPY``{}b{?>Ij@1`0@2-)2@ zgmSC@eUP~EFX~?6K^-C~d$As&YR#52ZJrGV7~ zCN+^A{GYfuJ}_(j#``m{XGDQtGxjXp8*he3f}mOSS6P7q$`$w^Ohuy~FrN-DRJU1( zg;Mb$7$1zlkd-bzjW!Ug!dTlZi&i6{YFj>Jd{}Nh7n45Nl$H1p@tiS%E++@%LILTb zy*(4f5*}-MW+tfBRBF+3?!zVzMNH&oB}x!UkEb;&@k)h^it(@8dq$opSnS#oc+-rK zeDANMl9mk3?rMp`)H#OJGwg6dDJvg^BJyr7v{ax_jVCJI`UTUllB+QlS>+O#ut-h^ z6UV64p{iZKZ0!z9RUkF7!qfaAyR~Hzk24H5c7dz5%#6bfi>vF=47!WN3kjv7dBdc~ z&@89(3vM&+ts$t_JJ;^2>SmrQ`?xJIT~JhVsDZeFgB@G@wIgzD?}kFJsj4gCg<5(| zLiZ^*<6+je_}V;2XF-%DhHPtlSPNab@Eikqk}vstgU!e9_OeaQYO{HcU)ppQS*2OOf2{zSb?{Pf<}TX=*|OM~?u$Ss zGQ1j)XFp%`MYb=iK#l`T(z=@xrS zp6sA80ywV=F{R9cOBuFBY&vk|Vj@UEoLd?~Ln9641P<4DS)sN%~PlUTRZsEh{A$!EwLjlro|7d7Y$$L;Yoj>_&hx74^T!tJmL?yY~}&G8on{)nW(_UPDUNE z+?h=mVMgI)+_SU;ypTUP?%i`y-UpoO zd*yT+Bkcmq-J{3y5Af#)2sUj}QR80R_phXGXHDl=V6H z%o=2kzC*3a-zqe&2%%oKRjykv+Q1yX2-bD*z$y@TQg{F^UaMGR`D zHwFvU+pSiKVf#Amu0%ZT@@1@&tdk)+ERK~TK-&4qT*mxp!bgoaOQDNa=ZWw}MiA zc(|SIE=wy7U1v=3t`NUCUZKT~dJz?@nG6r;pH!uz!3e zKw}CT=lhue&D{`MW4!a308K!Ngkh+XKwqK6_$dp}N}j|7JwfPJqmu4ee0J8AW56xV zk+zO!s5l+lU}=-G2xlZ*&%cH?BV^j_ry_c5{!UoGPf_sp;{t;C-IL&LmhWx7-AuvT zn$2XUr*fK|{6%PL-`_!tOHRq@>FEC}>735wX3%7o*X9Asd|?3;`Jbksw&t^3&QD8K z(j}n(p6(>sYyK`>B&c(hdqXW>)#JOmm z8`j&%%@M`ejkozTO5kU8`F*Uiul(hn>!j0J;sd$ zjL0TIW*gZ@dwu7_@CZvE+2KW_4d`tJix@2E6stpg<}X~n9v((F)GenI`G@??`w?lON{QU}-iWUct~+|Y`&AI(QQ(So$v z!CgSz{IB*@vrc4({J(>b?)xa4q-n!90NeEQhHd}ziz0Kvg8`d_7bulV6I1|Vvm9Rw~C;wUs^6!iKl#$3E^XPC8NN6S;oY`fzuGQmno z`Ot;H&_NvYgi(Nhpkty!5g--35VmiHGMG3PlTC|H(P~8kH^sBqXu-_AIElT{|F%nb zwUV2+R5vG}Oi6UEtlpb%udwi7p&)Q;rFgF_vIBFZ^_FW03tN0!$0-#+Zuo1yBCX*5 z>HO+&$oXT(k9!~|$t)gU1IMbm8@Wl|wIsF6AQGMyyP&^$Sm(U1=CJUNPTiDzg)hgaZz5UJJC-`sud$dQP2-)E& zM3|3nokH7YKi9|DVdOe#Rt=_~_6}R^L_35%!xk(=WPT`0@fV1P<*9d^ObvO&kRt zxpd^T*RX`dMMii^!~++4R|dGA_cb7Cptzy~YXGxYj!O+T!8xHh7vvxw`4!X(RVb;L ztd+BzxxVjL>bbt2g4qrn3nLh5ghmre^VmZzahOf?kPM6UjwdRXk0* zD6>lEF`0sQ<*S$ct6=08q&Ac-7IMvsA=|GN&Wg&5u`gL3@PL#bB--B$uBVg%Q7OC8tkEcF8_J#8yNXt_1 z+ohXq3FfRU2n>9UF^}3nv;v0HDG#W=Vn+JCaVk)3nxClfESVhHL7uT|tRkr5Eg}wJ zp|SP~y%(CzcF9Pb`(_%R4!vw6qWL!%7Cuol;(+(UmWWSm$N<|YAz=Cgr{bj=&MbPGq7aMPxgSZX9;))YE$lYRCBpG_3Z7Uff#PNM6x2lqB!Xbhj` zE>UWJ^9@$h#rE<=6Fr^@8jG{~M1S}`J1b4{qug}sXGnILdlPoVGQ>UqChZX7DcFCX zVRB;k-GNk}gRIpG>YJRAn)$2|jf{3H!b~#vHRl291g%{*^`GR%nF92@nfj~&E!Ys_ zOImOU4W_js7k`^+{RYRO^=!yhMbJ@(Fz&z{S(#M80cc?jyLDUQSlfcZ zTM(V#kA@bb?Ftb(@(KfCQsmc{`Zv%Ckgt6TnM(H7TW{vLKIUAb{6ZHeGgpqE{Njl( zf9cC7z7P+_Id2rMZBmSS^^-Bstx7MLhG3c+^=2zwfEgL=DK!6Q29H|?k8SJCnzQE5 zL{EOv4T3}e2_l;T!(Bc(VvZrd!Qb?;xx(BIe0Vi(1wde~G-;D)4}2GzwL$ zFH%RRF!fuy_qTgp~Uwn?LTDD!b#3)purxxsjf6^7tEm+6ENec^z zK#O;wRpu4%Z6EN#QBi$W3Y55Q^Ht6^a%^GI@J>$u)x**P3>Ao@3BGVayu=UO8M< zjFHaz_BmRnp-+9KiAzk_?QMaw`#5z!d3LpA>bb$T`QA3F1YT7g4hj|(+SR@}mcJB9 z{=9BbtXfrt`-v@NAuDke5zlc8hh+xVph?s6pfrqxbe4x4S+x2Cj*{oVg%RG0C5OX7 z0P;k9RHMa4jR>}#gHYFYzmtllLRIC&YpaX~SgHS|f{4?JCtIbeQj?S(1-G`U_Clr1 zL0Mvr&A=U$d4rw_lJ-b#uGdk}pjOVBD!V3w?cri;N({sVbTy{WhgqPHK}Kvhby7A8 z-E%W#jbit;%_(xio3K7nXJTqK;#vAr98h1$$N2BC(V4M^PDN+@ICtgKDa^zYjJa|% zb&h%_kO$?td_mny2eBIE;i#~EW4p}F);t@P-LCqrJZaoyi%MhHgdH+}O(D&{PaHep zJ8**d3h$yxXx_(TP#6rpm)Ry?LP6)JwXGOU+x*XU_m6ZDU1la)!?qtjZqgjHw|Dp0 ztzqBFSh&pVoIVmYR(d0Eij}lYmZ<5t`AX4;IT6I}c^_44WruKRWE9PRphW){C1p~M zvgRs57*WmtRgp@8nAPa8!hTVg|3P1?Eo)1bwb01yj$zf=WAZo#M;MUilSGfUG&X-( z8T{|cU^|ykp27%uTj?xc8;Vi0zWHZkY`WHnTlO47wg(>WwM(^U|7Sm zzF?IU+dc}FjL)`$zbgXdB{I(*f2{G?q~t5eX^_wyz$$GAGM5Lo90DoZ%`eJv{QupJ z2Pe;)`>yS=*|#B8)mYgls6T z+fr$Ir@mlGYrws#ir^4pgSM}*U|)42ipUaNL%>5`tR~Z8XJFS@6afH;2LNgs)GU;8 zA!r*%=y8JHUva-sP$L868ur$UN^0f)>xG5UP*cU9k)oPfiSa7gtUkfN%4}9RpIS?p zV_LQd{9`@x^UFPsF^%6psWGM|$hMiqrPdqGQ)`WX$*`7Pi95(Sq8pLW#)%?Y+=oPK ziKPfqyKai9knP#(cV#|;(WO(@(DHn9?()kG5$QWJU3c0^9eyH-TMS^OiR~qnF2uVN z*xR#50)fq)XlC9(^qQADS*}F?I~cFXx&ZRqtT1(6GL{Pv%p?Qx3~4Qzd_vip0SY91 zs2C?f^Pv&v%naSjHII&TrgjkI3W08i4!5?@(bDi0Q#xZ>2TX@=%y?v+VJ0#%+o;9Bo)og)m=5y_#DjYUdjD-`d{3J~@C{50 zW!)MxbYFiS2}k!qEP%?%Yo5b z(`!NY)3 zZnTNi!t2L0r~YGD1*;DYcEfn@l1uTak%U6-38p(QfYOE1nnb0Kc`J}fhUAVVun4I* zAsOa^3Di`&a^8gWT+Q6iFVQ3}p}jQMG&21>FF#es=azA^N|Ic9cQRXSdWbVTRnZuBSdT*uqp9i?-cDX}pecRS962%@StS|}3!HhY zETShAnkNv>Q5X@F@i%PEQF2f^SA}Vb{k)RrY zV<~h-qBO5LqYuPyHeby?Dy`xwCrleJ;+X8a?-m3KLV;{C{-$S6qzEs?A(u z7d>Qj3|1nCUZ{kG0b*KLy{3Fl8^hL|(A}gie^;3rgLXnWi|`8{HUE|ZOvqyrOq)TQ zMR}TLVc^=di5rjX8AsOg&xkJl5@pDq5w8~bvWaQ5=>o^r*t2y98V;P$IC-jZ1kUX- zbm<70k5Rs@WZ52~{$+ja?jFhMDN_776e9y^3imo_rPIb{T7J=AoP zs+YIa$d-Cq9X0|UozCBB24)(UkBO2wh4i}g2^fH0zJ^l=!oDup7u#hcElT$-`KS&_ z4L+u6DjJBEmN$3o&q!hCCs2+H`?wh*MvW_x6+;qPDQT#l zGJpJN+i+y8jX(3v;*`jlg~Aj*)Db{hWf7$3#-0X|VIx!2@1PIV&SQ;x80~*+vc(=< z1<6|`S9Z9cVJQ#1`T6$wH3PZMcD(b}vR2{tLYlYhjb^9(tu*6i=*VauqqP$>QmJfgb2|im!G#Z=ywqrH<{EPnWI$^x$zZ52PUWoKpq^;6 zRVHYa`SZzDTWqZWJ_cSdT|6-x9VxBq3JKRkdk7TP%oAFu6@ac1Vx|NTmGXW5j%a^DyQT^uXjd_I zWJ}MLL3(E%?{KwV+!ToTV<4*}XE95x{7=9|RI&1Qh4e~&6i`Afn22uDQ8uMMuE?W% zQc^N1qSfnEw4B~J7ssNL7}FdUl13SW`{*uvk-OP`$1u=b>J4xrmskW_7Bj&O`LAT1`IrQ*vcfhoG2ZR0ph9-P8jr6<5JZntN31HRTaiEGeyNzsC9n&2*=~V!lxiAW5oL zkcczLQfgrdbp1JK`ZrjV7+z^&TcPF_x~sh_y@lTJrUs}x)=3UnQd%ZNR)o`sB$cnD}c!01g+fSc7F>`_uky2BXYGrc+tW@K< zw21jA4<;ehBNjLIUP?^*wbn|J+^v*j%;!vESsK(-11V2RZcHu7eN2mmFHpHiJ@)yR3*&oEnv$aZ1lR#z9-h zgfuQpx1_3P5|m8fK~h#aw#0HuNo|~?PnZpJs-~23%s*#je(c>Mc)^sSXZw0E$f~T$ zZlN_ggKC2biLX&yak`~!m7-=-Xnh-mH7QhFBO`cM*Nyx&uTmP@Ffwi%YL=!}F%45O zPTc!TDi|2Uv9rr6;&oiian?}%?qTn<*7@Jq6D{HbzKf+T2 zal?b-esBIRW4pyIu^t!`eu!_!)kRHGt;+w#a|spRBT6U)@}(_cZqfnDrlk=Z|Lz9F zd}!i{#;kBy;c_a73F7|@H$&5cl{DM5Hz2#vI0XfC0X<}%bVUiA%s$GtO3J-W<|5eK z%gHg(_|R=gpyNOdr;PCXF%f=pR7{!{DB*Ux*_O>sW)DLS>MS^%5fT-*js`h;PEp5n zf(W87x1kf@LgXol#xIx$@#4<`h`ucXq|YiXb!g4g*hLN*D5NBJs+@3ie3qog?4Qww zixp+SQoD%jJ*2UN_JWv}MQq17>ti`=voYJXnc94b#faG?OC~M!^5;WYPKTLw@+dmm z3kZjNWlXe9Ok>0dH3pMV*q;O`=-V_?Q!5xh9E8s$;$RFMq!4u@PnNkWMSlSkY_(NV zn~O`_siXPpZawETN@POV>Z$O{FPNG05qVU`zMsplfSU#Ke&mxCYHbp3S=I0!w%hfJ6W`QvC&cTkaSTC2k|2SH-k z>s;iPIv^C)^_D}Zaxr1{C(a~_!c#VC?OGmjY?xtORgqQn-CaS9DUUT2et*!LM^AX6 zY+2()9bg~RC%%HZ&ZP+HJ1>@f$*C);ER5L-sv9H$B?}LJs1~^mqMK41hwRo_ATN3t zICon1aV&e8=LqW_$9*z_i9Hz6gmrUGNp2p>R%6t{JPBGlar&Oghbz)0*8sl(WaOZH zd|P1}qdM#D%b$-$@?$$deuH->KXd9|v~b+&S3uUvGC*>&;;hzS++tQK;9d6Wu@T}k zHUAjx{7|M*EsIzsU*nACi9uD(-sWQn{EYCa&YX&tY$$p2apIjf^v0yBuCPp4HocRJ zOPCLHC;cH-&>qg;gQU=0y_m7LWzpQvSIq;u7};h*Ym;(6uY_LVVv5mj^=hI$r;`3Z zz5ds_AXnt1L8b5%mpWY($_uiSWnPX&zzg#p6AM%%_2Ikhrr<@>o@N_&;sw;}pa!1<(J1lptO_3-hkMaFV48zVu9!aSXA+5*w`AQ5Z zHz`$ai%ONdO|=qV-l$XFpRNAw35Vx|xk1tyx%JUjG%Ekn#Jg+H7^G%?vISRk9})dR z17c9b$Cw-Oy_*%uj%Z5M;y$TGN;41zO7r#nE~=LM|T%U?9V-D9lK8~JN>#E9V<*NSffBgNw@>A!hYf3&B1v#(_n- zFfIQV#czOb3fo|vEzN@ic&we8wEYRab)* zE3LF%ajMk(T2scPa!`x*|X~W~L3$PYvjy;r;S_=cK9pK18 zna#jk&i@Mp9RLVg9jJ+^n7d1xk<)ByCV0!t2=U1;;cb9KH?|2(jVBV`zva9qBktX0!0l`TD_Tr^ zm69_hmsHgwS}hSUU>R)1QTiL<{E^E@$w}C|)D^na9`w)=F+BnyCS#RpVl!#lrGf9B zS~O-+BtUF7s>GV|su_6JG;cGGN` z=A;xZB5NMg{)!Ti ztB3k*)5c&Xs^wU>9n=_9D}UIbt^C)QW7D_>v*|KVYedOluaxm4D)G7O31&+kHJ|^mSlxj#JnGh} zvQrJes0R%L{A6H)T{C*m#GxHK@Hi@+=);XYTXt70TlW(4;MODNkMt0>uFgEqG-l9} z#w#hT^HgIR&v8#T%n=P>Jr0rs1O0>CI6W`nfm8KJ2;dYNVu$n!BRGTv?%WdLgTxW= zNyOlOQobr7_c2d{Wet|5Uu8PIE+2ETJN$4v@LDWCoN7v7KOptWk~yA3irmHCJ4g>a zn)ce1NI=<58EONauiA?3V9}DiHeTrukW}WcvN$KF*a=cKk%cXSDLdPKz06`Kj2x?z zY$>W1OWazrx0QA+FAjq8*J`hDRPLR3PH@mgILokBN{${n865KGcN9tJn*Id1Z{u$+ zOaHfSidLIY6;UnWqdVHM$mN9n+h~K6ID%H1`jbp+s=ID%8sKlXS{HwpiO085>KRoq zoIDV0N+pGP4Cpb3}wGg#V*59VbR+ql4R@17!296DyIHP36?J!k! zMJ#QUq@Qm-&zfXgrF-`=^iP|VEu2f;8~E@lZ2xj2h{L>U{(u&eB?|Wf z_5=3th6KTG>8Upqt)Oih>^m>-qg zpAG)Ngl<3tEh6!R2!ci-;s&N74RAjqdwS&axaIHX5l)x~W~yTt746bkIx^QQ`( z1PCZFE1arFCwoSzw+(?)#PG@bri*`EjIhk?BqIf;jhxXl*v`cbCQa4Dq`WX$WiTRpHSZ(y?^5@2AZapU)Nm`Xsbv(Mp z?pmusT0SnMv(Y0h=9;yJCsH?qTxr9#n!Dv z?}EZ?ng{vkQcju$>;F;0T*gKGqB%JJZ;p)3i_Sy95MK0 zJES<~&CLw!g|0Ps5@xIpaDeoak(_3klu!%O_v`Y2E?vF&vhGgn@@MrZ+^OWI3|*=;muRi%sG#KB3<1MQvH35k1Pi`$ z1k7I*%`>E40mf4IHXl^Be@g|}tGlyYHjOirnfeGURRqoV2uqz4mo`Vd?0JKn%zc_* zDLGR+0>q>PYZ4eW3B0uR0Xxn{Ak&^LI^5=2z^M>p%b~_0v^O3x5KCvV?`}*_93h2# zYX1a(U6wIsuZSiAydwOR^NMg=__1;pY_IYXYxa<7Yaow@Kcw8KNz5Db1xIZBWa$YD zo+`1ZCATM5hc_JC4nO8<5~k%Lbq@|&=Xi2TMCBT?@B%o6)zcMDiR05vlL5|0S{^6^ zpsGN&`@*AG!)kmO#Es*lW8wN8V~VMiOxpu~O$?fsC50HfauWOggJ&uDy(e5YT$sj1bIF+8t_=Qie0J8_;KZ4QR0wf0i3LELIgk}nlDci%Ae3kR1RTAe5i&-jh$S<3xk$TCrTj;vR z`#%L!OZ`b5R+S_)oEx}_3xN!z6v(vpm~gr&(6wl|?o^9jFc&YyQ>e*TnOZ*-qde5i z%|mD1hEzUD1@omfwZg`(`7r8l1oJ1U&Mr%rcy^_7dP%V03u=uiU1bt?^B|&iW#`0m z!z7D{@Uz)rv*U3nNRrWg=-A#}_s?8W*j4iSI_3vaSJ4KyZ>w6pb@5Z zKt`-Wk@&9R7*%Oy70+0&-BU3>fyTDa%sO+B(u+f=Qp&q6+lait$70Ihj>n{Zac|6f zT;Ndrh9m#XW1M-6b5Yg;qWMxHB-~Uc*rThqge$4%61N@4#@(>dm98im=me^YfBaQs zpi^P0taGg}_1Pi8u5jK>Vx8;i{{f|H_b@w)#t$&&Z^YvRyB#wqJk7R_w%yV&I9XbM z>y1bMq~m_laV5bOMcv;+)z80^08yTeijSFlrx6g3K8yBBwKHQXJG>JMaVarHQ`DbQ zG9x}Od^l|k0~EgzKInfTM zi{@*(8-X&`!zvt+_=dhHVF@v}>%$V}4sbne!gOLv{Ad)aFzuL)@rntRWs(bowmIfE z3b|$CUki#p+&!rIhT{LAF5lEeAVu?Ax|FDd011O8J`GgD$aCpKf%w?b#(_fkFNI3{ zMyim#x{kUVGzGKLSNu^m(bsi(MVB{qX>uuB%a;Kh`*L}VPrAjUWd zfI+hG@BoTosNw3LHox?sJfK(;0C<1M(ls3q1|qPbOV2fuNs~te?i69du$?tGjJRtU zyfYJsst0lex4@wvrAVZBANUfz+AtX8etQA9VY(uU3eE(Yf=v|~ z8G=u`z$bjmhae^CJA?5q*bx=Q|HPVOnMV(U7V{Gy)ualgRk1s~+Q;|SnfyKcsEK`V4`)6&HDiNgrfEy(n-=Fi zCyRZ-7mtMUNNOW_N23{8Xa&7hy4%$tc!NI#aSr;>H5n8=1ctFb!sb>7R}^kgP;)2n z;;E{B9?N_E3}4S74uMq45NufSa&KbBuMPX86*JzGQpM@FZH4E1lFd1hEJ}gJ z%>UR4ZxuP4iQTjpHh+XAwA>$^ew|U+-_cv6M!(P9wANjtMs0JeO&He!mt1RY54&#l zF5Xo}n%P8GXN#%BS0;?*QEN>5q}qR`!=5LJ^|$us2$%zCyny4vx=bivtowJZ783Vx4WC{$v|{gWRrfPH;llL z51vH&Knp-8;5!lWE7CxU>E4k<-L*8xQ9>UUe(L;Ty)_R+h#_nS_)8u(!&<-&nEENT zu%>bbQ8_EpDCd_*q!ZLfa!+^Xb3``?uPR6Mn7z5Ep1SU=LUv}WzheXK*MVuj?lSGy z>LELB7jLSaBF0Q}HHh4hPc@Xk2$;PlhlAEZEM_gMBYTkeAO>^oRwx{o`t43O+}uf8 zg{rEHJTZ8tJFp@!GA)FoG)lM$RY~t%=(A|1F7YEDlJI`JK^jUy5b${*hRfV|AV%9r zPrcPHHx!P9%?#spZ)f=+BSmnmM>H?FVufy>@Rdv-2zkgY!4V%*P1j9DBx+{&Y6L2a z{-}QmIcUAK(U(DV=?#Ytt65fiKIvmSa0v2}qHd2uS43`PvftEMQDMc(oLVEWBoMli zEAcTbk39qRs6ekH;y6TwPar1eKZsXeASHcRB&O~qPe%HQX*Y_o-ny9Q^T?GZ4I%Qd|7@EX*4mHi#5_n72 z<7Uo*Q0=pWjmm@U6Lc78t}69?C-TSf2gk|=ygV>A(OdVfID1#wZLBWaS@vB)2RlTs z@}WI@r^=Nx7DZzq$vdY%z+~@eRYT^EN)%?xT@+<=rJY{AaazK@0Df-Fs_gx15JB@8 zyqSDvxk|4I(<)YjE^Fg$HZ8_aXp%n?k>F*f9B_4 zy{0=M^G)$x&0o^xKh@&BuCT+p{Gu*87Hhbli?7-?_{v>2viYNXT{Ko7 zzs{GMz9!|WPX)7p95*6Mgra{Od;CpEp`m_RF-?}Jqc zj!D7Ml3z*G)0a0k6P(H#jXR)FNLc;1Vs-Oq|OzDU&Uc=e1h3H_e2bHH@c z^W%*}6Nk5QBA8AJ<9VjBd*Vridpb#Mf8)8v&Ml82%#-`L`%q)P^Yg<#rja)nPq|Kt z-&d9R8J@Cx=kPIZ6P|K$fUAewVJT1X#aJnl#1Sq+Di3}BeMG(ukitRAv)6^b`k~3J z+fAvn02w3J2Ui;iB=vFC!4J zX~@m}fHWm?qfXTXPRPxFM0W?_7Cqxgh)I^{D2Y!IVIk&m>*beqBU}ezQqW5`&Gjm- zvpU^CKm-F!488;dfTuop`N4)jn(A@O?(jJsw`}&O2$cdDvnzoy+zE`~4NQ8{6i-MH z&;n95z!*boofCXRV9vsMaTctgm)q-*UP%9#WYG=!i%H?m4*=)F7T>yd~JB% zM6n`2EE&Km;Tq|8$f3Atk6P&7YkPaT$-UfqRfd(YYJGBME>KxW*j!*1+W*C}{j1dc zC*bH(v)7LxzU!lhQ#wgO(_le$=3N{;oKh(rz(h;Nl2Jk*$(A7>LD%l-fkuh9`Ssj4~pI-|G$LKm4LR`?xbYj!CtDf;=5zuNjoc^k1< zOy=0ujzm=s0u~bxU&fAbE5*`4F;iZDR*5H-KDS5|(@~lWmXzu}3NrVRY@p1&XkpXa zD`H1{Uv?O3TE>CU+Ee9uy_1J90<50bYn1lH?M~_a3946?-R|zK0?pqBFW-`o%p0*UbB2+$zNh%}M?7K?Em z%E#i&naqj^kWazq)oUd9hECHoKW~7WI^Z0Drk|f;kC10dJHTkrUF7u6yvIqE(IRL; zT4NG5c%&H{%d;exiM>}P9#V<_Yi?4BB{ZXuCO3l~!*tR0@u&$a)xHhi6 z<+T{El1&_L?3~yJ!~68)GZW8D+W1kp8q3Cy1``b*<=%Q{rJ7cZrb@aQ zh1^bJUs-4`Ug@;IwoKvZd=j2i7S<_exHCKh;Jj05q~8_uuY{@L?y=3pUB@Pq@{&%@ zU1%?-_Twz=;F)Ujp~l_^VO&&!*t)aoMEz8LQzwwKNS`r`?Sc8pX1*~+E&#?b*Q8Qw zQ-+`)BSh`3kIy%MN|kwr+ix#jU+UjjvJq`3`BI)Z-p%FQ3;B=U@)rBOeAATqy^U8t zz@^eY7gL)NU+#P3r*WWV9Gs1cee#}+|8Nh4l9vg%cA5dekJyus@O=(>S_hxJFJN8EthSzKtWTUqvfvN*H zw)2J%e`O&PC^!>!-qd+?-IcBWveqeSGnpdJxyygkk~H9koe6YC>d~fQ;hH!fO*Zgd zfc}_XQFMSCwq`=e7F`;YdH|wSUQ@OK&B246Y5ky^Oj7%RT=d@rI?Doj&`IVTFhMc= zH=)SfqSqYJ93=}{WLF~-yn=hOhYz7NBvfeKm|k&K0^}Ao#qBgU!>|4dL%W7wja$e3R5tb$EN^v)}c%v6oYp`)}A55L?JZtc<^(>CvsyBcj z|E7RYwxNEaDpvNQs#E-mhVZ7U)LV8VSe5r-@Nu)zDQs0>#bMlI?g+nz7M6-0IyW%K z9Yc5Ew!qUjIrsf&qDsn9jfZc@ZO!#}SM{}ORb<9Xh9;*eenHf{Xs z0Mu*^=TfX&Ng+tS^{3*wy@q%lsTj!H8g)9E_vXa55(1A~SqdTFaJSDr$CA zTVDK!llSP0%TtJReJ0cv*XK^HOr2gxlJQ$otbX;v#n$;(-+28SXWwczf0{SVpVj4$ z>l0%cjnPb!Zab;Qfgm&Sd`FSxpuG9(bcgnF)`XwWnr&TvSuf;9-FV0_W&`44qT{l6 z6F@Yp5C4F^5e;(_s>^OxYh~Lka?He^0d1;H*jU-0q;SQ{6e@evbmo!d%9K+Ff=PRR5Bl?aVSSPQPJFIm ze>q-tx9Qd&z;)FD;_R4s5Y2isG90zu6hGGdQ#5B;AG^CZuCw_wr1+yMxLRBw+3x%7 zEP%u~T?EwVbDHTVW}QAZvqAW#z(bV+UyY?PtdkF^paSHqlkL5sAYt_?MZk8#k(;ty z#X*i6??~SWBdlTjdKWf`g@d)7)xg#ktu=v?@bf2w^G;TRX;v6z#<*HkG*_PVDIa%h zD~y0J___gyd-QM_pk)UL(!VC1;n>0=L)MZIe@4OA4IUGZC)3wa9xpV^r^VI%Dj8Y+ zj~Z=if^3^v5J;&;)7o0&uQ41U-@ys5Z_frtbR$VK?2Y8m(SO=JtVP{|VD+1gwLa0X zmgJjrmuHOSSmQaHbw|99Nn+ttSaYDKd4y9DAia(B5A@6gO$H54@B=h1;W(Pj9kq(_ys#2yZFm2FM;TBLVC^o z@cHCh@LF~D*CL%?o43%YBcyzQJecszedaL>HSGH`0A55L*38Vx#Er+|8cFc)j@J0U z1METI>X|_39ccRMGA%DQp{Mm_`nJoNXMrMK%uSI*aFm$2HNEn4M~;h!9kw zI}vz((Cb5X&JEg&;pigM9awA+XRod)a?s+xgCdvm>q>c1>yxZUn`2}#MJ{t@wHFz* zu0Z~ew{6Or_A^&QShu9>sQ|3!728JK+Ee9HlW6LWh=n&0y4q(WER{16RzsZasCpZo zc@enX7eDhIg^oO@Al{7n9o~*{(JWb1Ad7{*2rSdq7pWAuw^Hh^BbW)NFH^rJy^^SK z5EBklNAZoN3uAHCJi&|(LpROMD+bA&`)+gP_Nob~cvW?TS{X;i-KaW3>0Q@kbycs$ zqQ|HAarp#7{3LO@Q%SzLoE#D2o4DTm^#b}-m%P%BI<6?C?y>0;a~!P>@6y}YB{2ba z=p*5r`PVV?XBhj?|#mt zdbF`~@)TOO_a!~s+sXg`^rmxPQoH=?Dzb53Q^^1K7$<%1)r-w_q*RmC?JP`r@p*2# z@Kz?et9od08d2QVM0N=>(MU!JOg);z7O~mIKj-3EBr%b}H_AGnURNFV^D`$;ed&v* zjvDB3F4@#vL>;>j(}c`xT1gxQq-8XLr-li{Zeok9IHlTJ4*N8Vt25RJ-c)St0*58c zrrYKm%sEdHI*!$R_9`c2jU+Xbe?yAEhIEk{Cl9TagTaB(4qIQ{&!c--mU_Z!-koCSUr>cE*ALm<&Hu z#*aP1J%P@T{TwX?x8=>_;^K3cv2cbRK!VKCLMAQxbrae9$Y8MPO;LlwFHvC~klTRZCmSX&a&_VWVPnuJh7aQ#`u z#BN9&!b7k)Mk>RD9bj$;O!Htz7d77ho38f@mn1=o4mMYrd2~%^Kb)FJXHGF&_P08+ zNH*k1dJwr_!i?!No_@mM{O3&Uvo>Tf7q%fNe@mJT$h!PbANSGQA&f8 zZD+d!PMu!j+~2rjH@>KLfhGqM@h{4(_j-{rUAiBh_ym26G< zGo)+#UwyEwmSbdP$#*o2f8^Pzwa`X!u59c&KjvJX^?R9lr^Shioz|&vtRhF-=@i@^ z9;av|ShPY+7ZWyeh@;ciJ2<7scI(8lZ-=?bFJ|F%7)rn^Xrd`b!H{VW;v7FZlXCzI z+4+!MT&Wz6IJ^oE%D2X;4trNn*+?aMj~*E_%HG>Z_j-1rh-8^e7sGt8B*TQLP9qJQ zxZJROq*_+zNeuPxlzxTa-}LlZAvdA?T0Lj`oo*}XR!6qnYoS~|X)P}M+&FKxAg;9b z$Jx0nX97!S(Or>hk8i%CODX=*R&OkXs}0r``{qya(oO#q?wxN%ZvOlF?5Fiw2Adnq z3zOZ%PBod1PweK;D21QZ<@f3G`?-K5qPZA~z_5k=Pj#NMGY7dv=mzLG+V6M}KwTh<(*ipl`m&2Mt6nr#GFV9 z+sU>5Xj)YiX4?5Y7!Py>;&RA?a?+V(ySf1;rwPz=w)<5g+X^jaF6fuo$4++MaS<(z zS}`G9LH=;3X#Y#KhCQk3XCF9Uww&gH4+b-zc6Gy>g!uN*otd9OE5i2GO4W+1{gs6d zYT8nBSVC3t3fewuB@^2zy@h^JIA5Drs_9TE$gO=ENC17;Ub=}Ek+@Obd4sk`D(+3> zJRuGJUUjLAdt=-}tKx>T*xESH_E*0JWdOxac=X_SzEZ@nSH_N5_nS9sj5haRF^zA)Tgck|XtZAf!x^%s9#O@5&z}bEsPV z^Vkp_)07BBV?L}b0)ilU?k%+k=H3*-YLW4Y*|PkGrmxqQI!DCJm&^U4Cs5XXVdm4+ z@+9NbA$hW@P!H>1vTu%poe^9g_EYv@=4xOw`^pg*=DosAm!>EFV=y|BtbtQnk)9TL zDT)$e|D6*|%9AsfXYx1Za&IN&6Ju%)MeUtfwLE{_JiagB2Y;y!8rdLp>hycvK|fZ< zmnff`l`5oiv8MP020K!Y?Hjt%p4Bxk6lo?ugTo)W^Q4?NBjuUr?^Vdo_iW93uR_8R zLW+?~1M_lq?@IcqFsbU4Whuu+4zY{&Tz8nT_v&&%_xPC7>|U4G$ln>mW0X1GHLsDT zR!ZaEcnzkf@faz&alA&Qv7-A|6IZEamuUyui7wVeKjMS-NJ=hhbmr5({$Fh~ZNf^T zxgM8Dv0-a?vAFh+NRmd_m<`%qIg}HpzHpTT$Y;KaAJ-$7GKS@ssTzcZ6&5TlD5<8g zevy+i&bF*xbzzJ1sWNbHGl=f2BDMV}j^@Oac3I`b%rf^YEf~u(!du;WqoxL630Q}( zs!Yebs^?|5BR11aQ|2=PPxCp*ujYGP+=We>JCI8MQ(AeRpMH!W=fRHY?u;&`t7kme zOL{aj$Qj)Uyl;L%moMsKhdLS>d|Hpfdz(L?%U5){qRWfA%<3X=)=!L(`KOt8LBlMe z5$5nKaBQJlEU$_=j|{0$tbL0vA(m z9NzT*R{g)L{@)E}&x~8UsQAXReMfgc%w^Nqvv)}BC&ZqK8_Zr5OZGgbKfC#V4{mBv zsYCQ5)(qz=Fm1c}e=om7ji-sBzwrdf^#9~TgdQS%FRFUy*Y$4e@;z~f#!;6Qvb%fK z$on(&(Mzh8x@bx=p`DzN@C_KxSC!!nF2*ajT5~JIl|i@Fvg*qy2)kRom32tbTb+^l zX5pHn;{IW&xR|__`F0~Y!aix7xv#|5fghZ-alBsqQy%G5^V?);5P(Z-TZvnkiIa0s zG$m|mO1x#>lh(w9%<4A96Z^1M(cPcZF5RgKnubzHO|E%Rm-}_m)G_BuYZ8nbd~=WD znQ%qpe@#63n=f4NR%Sddc6~_6s<1uC^SP5vwOI2znlxG!nh&eOdhMw-_v^Jy@871o z-=T{}g|(c+JHA6j9I5&9it-b>{5QJ%f-ZkUm%pvcKhWhL>N2J5_UU3uzVdF|ysnEZ zhMQ(=XX?FD-)u^X*wnDu)PZQ=%~x=&DI#Pad}R%4B?K zV)w+<01`5FWHho&ZX%!ETge%p=*cYJxr{v8JnymMflIKPs;v18YPFC8cj PT-fpO?(GLI9_ar6!@g0` literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/core/target_assigner_test.py b/workspace/virtuallab/object_detection/core/target_assigner_test.py new file mode 100644 index 0000000..09ccab5 --- /dev/null +++ b/workspace/virtuallab/object_detection/core/target_assigner_test.py @@ -0,0 +1,2505 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.core.target_assigner.""" +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.box_coders import keypoint_box_coder +from object_detection.box_coders import mean_stddev_box_coder +from object_detection.core import box_list +from object_detection.core import region_similarity_calculator +from object_detection.core import standard_fields as fields +from object_detection.core import target_assigner as targetassigner +from object_detection.matchers import argmax_matcher +from object_detection.utils import np_box_ops +from object_detection.utils import test_case +from object_detection.utils import tf_version + + +class TargetAssignerTest(test_case.TestCase): + + def test_assign_agnostic(self): + def graph_fn(anchor_means, groundtruth_box_corners): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + result = target_assigner.assign( + anchors_boxlist, groundtruth_boxlist, unmatched_class_label=None) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0, 0.5, .5, 1.0]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9]], + dtype=np.float32) + exp_cls_targets = [[1], [1], [0]] + exp_cls_weights = [[1], [1], [1]] + exp_reg_targets = [[0, 0, 0, 0], + [0, 0, -1, 1], + [0, 0, 0, 0]] + exp_reg_weights = [1, 1, 0] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_box_corners]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_assign_class_agnostic_with_ignored_matches(self): + # Note: test is very similar to above. The third box matched with an IOU + # of 0.35, which is between the matched and unmatched threshold. This means + # That like above the expected classification targets are [1, 1, 0]. + # Unlike above, the third target is ignored and therefore expected + # classification weights are [1, 1, 0]. + def graph_fn(anchor_means, groundtruth_box_corners): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.3) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + result = target_assigner.assign( + anchors_boxlist, groundtruth_boxlist, unmatched_class_label=None) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0.0, 0.5, .9, 1.0]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9]], dtype=np.float32) + exp_cls_targets = [[1], [1], [0]] + exp_cls_weights = [[1], [1], [0]] + exp_reg_targets = [[0, 0, 0, 0], + [0, 0, -1, 1], + [0, 0, 0, 0]] + exp_reg_weights = [1, 1, 0] + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_box_corners]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_assign_agnostic_with_keypoints(self): + + def graph_fn(anchor_means, groundtruth_box_corners, + groundtruth_keypoints): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = keypoint_box_coder.KeypointBoxCoder( + num_keypoints=6, scale_factors=[10.0, 10.0, 5.0, 5.0]) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + groundtruth_boxlist.add_field(fields.BoxListFields.keypoints, + groundtruth_keypoints) + result = target_assigner.assign( + anchors_boxlist, groundtruth_boxlist, unmatched_class_label=None) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 1.0], + [0.0, 0.5, .9, 1.0]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.45, 0.45, 0.95, 0.95]], + dtype=np.float32) + groundtruth_keypoints = np.array( + [[[0.1, 0.2], [0.1, 0.3], [0.2, 0.2], [0.2, 0.2], [0.1, 0.1], [0.9, 0]], + [[0, 0.3], [0.2, 0.4], [0.5, 0.6], [0, 0.6], [0.8, 0.2], [0.2, 0.4]]], + dtype=np.float32) + exp_cls_targets = [[1], [1], [0]] + exp_cls_weights = [[1], [1], [1]] + exp_reg_targets = [[0, 0, 0, 0, -3, -1, -3, 1, -1, -1, -1, -1, -3, -3, 13, + -5], + [-1, -1, 0, 0, -15, -9, -11, -7, -5, -3, -15, -3, 1, -11, + -11, -7], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] + exp_reg_weights = [1, 1, 0] + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [anchor_means, + groundtruth_box_corners, + groundtruth_keypoints]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_assign_class_agnostic_with_keypoints_and_ignored_matches(self): + # Note: test is very similar to above. The third box matched with an IOU + # of 0.35, which is between the matched and unmatched threshold. This means + # That like above the expected classification targets are [1, 1, 0]. + # Unlike above, the third target is ignored and therefore expected + # classification weights are [1, 1, 0]. + def graph_fn(anchor_means, groundtruth_box_corners, + groundtruth_keypoints): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = keypoint_box_coder.KeypointBoxCoder( + num_keypoints=6, scale_factors=[10.0, 10.0, 5.0, 5.0]) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + groundtruth_boxlist.add_field(fields.BoxListFields.keypoints, + groundtruth_keypoints) + result = target_assigner.assign( + anchors_boxlist, groundtruth_boxlist, unmatched_class_label=None) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 1.0], + [0.0, 0.5, .9, 1.0]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.45, 0.45, 0.95, 0.95]], + dtype=np.float32) + groundtruth_keypoints = np.array( + [[[0.1, 0.2], [0.1, 0.3], [0.2, 0.2], [0.2, 0.2], [0.1, 0.1], [0.9, 0]], + [[0, 0.3], [0.2, 0.4], [0.5, 0.6], [0, 0.6], [0.8, 0.2], [0.2, 0.4]]], + dtype=np.float32) + exp_cls_targets = [[1], [1], [0]] + exp_cls_weights = [[1], [1], [1]] + exp_reg_targets = [[0, 0, 0, 0, -3, -1, -3, 1, -1, -1, -1, -1, -3, -3, 13, + -5], + [-1, -1, 0, 0, -15, -9, -11, -7, -5, -3, -15, -3, 1, -11, + -11, -7], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] + exp_reg_weights = [1, 1, 0] + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [anchor_means, + groundtruth_box_corners, + groundtruth_keypoints]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_assign_multiclass(self): + + def graph_fn(anchor_means, groundtruth_box_corners, groundtruth_labels): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + unmatched_class_label = tf.constant([1, 0, 0, 0, 0, 0, 0], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + result = target_assigner.assign( + anchors_boxlist, + groundtruth_boxlist, + groundtruth_labels, + unmatched_class_label=unmatched_class_label) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0, 0.5, .5, 1.0], + [.75, 0, 1.0, .25]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9], + [.75, 0, .95, .27]], dtype=np.float32) + groundtruth_labels = np.array([[0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 0, 0, 0]], dtype=np.float32) + + exp_cls_targets = [[0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0], + [1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0]] + exp_cls_weights = [[1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1], + [1, 1, 1, 1, 1, 1, 1]] + exp_reg_targets = [[0, 0, 0, 0], + [0, 0, -1, 1], + [0, 0, 0, 0], + [0, 0, -.5, .2]] + exp_reg_weights = [1, 1, 0, 1] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_box_corners, groundtruth_labels]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_assign_multiclass_with_groundtruth_weights(self): + + def graph_fn(anchor_means, groundtruth_box_corners, groundtruth_labels, + groundtruth_weights): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + unmatched_class_label = tf.constant([1, 0, 0, 0, 0, 0, 0], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + result = target_assigner.assign( + anchors_boxlist, + groundtruth_boxlist, + groundtruth_labels, + unmatched_class_label=unmatched_class_label, + groundtruth_weights=groundtruth_weights) + (_, cls_weights, _, reg_weights, _) = result + return (cls_weights, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0, 0.5, .5, 1.0], + [.75, 0, 1.0, .25]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9], + [.75, 0, .95, .27]], dtype=np.float32) + groundtruth_labels = np.array([[0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 0, 0, 0]], dtype=np.float32) + groundtruth_weights = np.array([0.3, 0., 0.5], dtype=np.float32) + + # background class gets weight of 1. + exp_cls_weights = [[0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3], + [0, 0, 0, 0, 0, 0, 0], + [1, 1, 1, 1, 1, 1, 1], + [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]] + exp_reg_weights = [0.3, 0., 0., 0.5] # background class gets weight of 0. + + (cls_weights_out, reg_weights_out) = self.execute(graph_fn, [ + anchor_means, groundtruth_box_corners, groundtruth_labels, + groundtruth_weights + ]) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_assign_multidimensional_class_targets(self): + + def graph_fn(anchor_means, groundtruth_box_corners, groundtruth_labels): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + + unmatched_class_label = tf.constant([[0, 0], [0, 0]], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + result = target_assigner.assign( + anchors_boxlist, + groundtruth_boxlist, + groundtruth_labels, + unmatched_class_label=unmatched_class_label) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0, 0.5, .5, 1.0], + [.75, 0, 1.0, .25]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9], + [.75, 0, .95, .27]], dtype=np.float32) + + groundtruth_labels = np.array([[[0, 1], [1, 0]], + [[1, 0], [0, 1]], + [[0, 1], [1, .5]]], np.float32) + + exp_cls_targets = [[[0, 1], [1, 0]], + [[1, 0], [0, 1]], + [[0, 0], [0, 0]], + [[0, 1], [1, .5]]] + exp_cls_weights = [[[1, 1], [1, 1]], + [[1, 1], [1, 1]], + [[1, 1], [1, 1]], + [[1, 1], [1, 1]]] + exp_reg_targets = [[0, 0, 0, 0], + [0, 0, -1, 1], + [0, 0, 0, 0], + [0, 0, -.5, .2]] + exp_reg_weights = [1, 1, 0, 1] + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_box_corners, groundtruth_labels]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_assign_empty_groundtruth(self): + + def graph_fn(anchor_means, groundtruth_box_corners, groundtruth_labels): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + unmatched_class_label = tf.constant([0, 0, 0], tf.float32) + anchors_boxlist = box_list.BoxList(anchor_means) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + result = target_assigner.assign( + anchors_boxlist, + groundtruth_boxlist, + groundtruth_labels, + unmatched_class_label=unmatched_class_label) + (cls_targets, cls_weights, reg_targets, reg_weights, _) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_box_corners = np.zeros((0, 4), dtype=np.float32) + groundtruth_labels = np.zeros((0, 3), dtype=np.float32) + anchor_means = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0, 0.5, .5, 1.0], + [.75, 0, 1.0, .25]], + dtype=np.float32) + exp_cls_targets = [[0, 0, 0], + [0, 0, 0], + [0, 0, 0], + [0, 0, 0]] + exp_cls_weights = [[1, 1, 1], + [1, 1, 1], + [1, 1, 1], + [1, 1, 1]] + exp_reg_targets = [[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]] + exp_reg_weights = [0, 0, 0, 0] + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_box_corners, groundtruth_labels]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_raises_error_on_incompatible_groundtruth_boxes_and_labels(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder() + unmatched_class_label = tf.constant([1, 0, 0, 0, 0, 0, 0], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + + prior_means = tf.constant([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 1.0, 0.8], + [0, 0.5, .5, 1.0], + [.75, 0, 1.0, .25]]) + priors = box_list.BoxList(prior_means) + + box_corners = [[0.0, 0.0, 0.5, 0.5], + [0.0, 0.0, 0.5, 0.8], + [0.5, 0.5, 0.9, 0.9], + [.75, 0, .95, .27]] + boxes = box_list.BoxList(tf.constant(box_corners)) + + groundtruth_labels = tf.constant([[0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1, 0, 0, 0]], tf.float32) + with self.assertRaisesRegexp(ValueError, 'Unequal shapes'): + target_assigner.assign( + priors, + boxes, + groundtruth_labels, + unmatched_class_label=unmatched_class_label) + + def test_raises_error_on_invalid_groundtruth_labels(self): + similarity_calc = region_similarity_calculator.NegSqDistSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=1.0) + unmatched_class_label = tf.constant([[0, 0], [0, 0], [0, 0]], tf.float32) + target_assigner = targetassigner.TargetAssigner( + similarity_calc, matcher, box_coder) + + prior_means = tf.constant([[0.0, 0.0, 0.5, 0.5]]) + priors = box_list.BoxList(prior_means) + + box_corners = [[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9], + [.75, 0, .95, .27]] + boxes = box_list.BoxList(tf.constant(box_corners)) + groundtruth_labels = tf.constant([[[0, 1], [1, 0]]], tf.float32) + + with self.assertRaises(ValueError): + target_assigner.assign( + priors, + boxes, + groundtruth_labels, + unmatched_class_label=unmatched_class_label) + + +class BatchTargetAssignerTest(test_case.TestCase): + + def _get_target_assigner(self): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + return targetassigner.TargetAssigner(similarity_calc, matcher, box_coder) + + def test_batch_assign_targets(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_targets = [None, None] + anchors_boxlist = box_list.BoxList(anchor_means) + agnostic_target_assigner = self._get_target_assigner() + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_targets( + agnostic_target_assigner, anchors_boxlist, gt_box_batch, + gt_class_targets) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2]], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]], + dtype=np.float32) + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + + exp_cls_targets = [[[1], [0], [0], [0]], + [[0], [1], [1], [0]]] + exp_cls_weights = [[[1], [1], [1], [1]], + [[1], [1], [1], [1]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0.15789001, -0.01500003, 0.57889998, -1.15799987], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_boxlist1, groundtruth_boxlist2]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_multiclass_targets(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_targets = [class_targets1, class_targets2] + anchors_boxlist = box_list.BoxList(anchor_means) + multiclass_target_assigner = self._get_target_assigner() + num_classes = 3 + unmatched_class_label = tf.constant([1] + num_classes * [0], tf.float32) + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_targets( + multiclass_target_assigner, anchors_boxlist, gt_box_batch, + gt_class_targets, unmatched_class_label) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2]], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]], + dtype=np.float32) + class_targets1 = np.array([[0, 1, 0, 0]], dtype=np.float32) + class_targets2 = np.array([[0, 0, 0, 1], + [0, 0, 1, 0]], dtype=np.float32) + + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + exp_cls_targets = [[[0, 1, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0]], + [[1, 0, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], + [1, 0, 0, 0]]] + exp_cls_weights = [[[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]], + [[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0.15789001, -0.01500003, 0.57889998, -1.15799987], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [ + anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2 + ]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_multiclass_targets_with_padded_groundtruth(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2, groundtruth_weights1, + groundtruth_weights2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_targets = [class_targets1, class_targets2] + gt_weights = [groundtruth_weights1, groundtruth_weights2] + anchors_boxlist = box_list.BoxList(anchor_means) + multiclass_target_assigner = self._get_target_assigner() + num_classes = 3 + unmatched_class_label = tf.constant([1] + num_classes * [0], tf.float32) + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_targets( + multiclass_target_assigner, anchors_boxlist, gt_box_batch, + gt_class_targets, unmatched_class_label, gt_weights) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2], + [0., 0., 0., 0.]], dtype=np.float32) + groundtruth_weights1 = np.array([1, 0], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842], + [0, 0, 0, 0]], + dtype=np.float32) + groundtruth_weights2 = np.array([1, 1, 0], dtype=np.float32) + class_targets1 = np.array([[0, 1, 0, 0], [0, 0, 0, 0]], dtype=np.float32) + class_targets2 = np.array([[0, 0, 0, 1], + [0, 0, 1, 0], + [0, 0, 0, 0]], dtype=np.float32) + + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + + exp_cls_targets = [[[0, 1, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0]], + [[1, 0, 0, 0], + [0, 0, 0, 1], + [0, 0, 1, 0], + [1, 0, 0, 0]]] + exp_cls_weights = [[[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]], + [[1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1], + [1, 1, 1, 1]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0.15789001, -0.01500003, 0.57889998, -1.15799987], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [ + anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2, groundtruth_weights1, + groundtruth_weights2 + ]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_multidimensional_targets(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_targets = [class_targets1, class_targets2] + anchors_boxlist = box_list.BoxList(anchor_means) + multiclass_target_assigner = self._get_target_assigner() + target_dimensions = (2, 3) + unmatched_class_label = tf.constant(np.zeros(target_dimensions), + tf.float32) + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_targets( + multiclass_target_assigner, anchors_boxlist, gt_box_batch, + gt_class_targets, unmatched_class_label) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2]], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]], + dtype=np.float32) + class_targets1 = np.array([[[0, 1, 1], + [1, 1, 0]]], dtype=np.float32) + class_targets2 = np.array([[[0, 1, 1], + [1, 1, 0]], + [[0, 0, 1], + [0, 0, 1]]], dtype=np.float32) + + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + + exp_cls_targets = [[[[0., 1., 1.], + [1., 1., 0.]], + [[0., 0., 0.], + [0., 0., 0.]], + [[0., 0., 0.], + [0., 0., 0.]], + [[0., 0., 0.], + [0., 0., 0.]]], + [[[0., 0., 0.], + [0., 0., 0.]], + [[0., 1., 1.], + [1., 1., 0.]], + [[0., 0., 1.], + [0., 0., 1.]], + [[0., 0., 0.], + [0., 0., 0.]]]] + exp_cls_weights = [[[[1., 1., 1.], + [1., 1., 1.]], + [[1., 1., 1.], + [1., 1., 1.]], + [[1., 1., 1.], + [1., 1., 1.]], + [[1., 1., 1.], + [1., 1., 1.]]], + [[[1., 1., 1.], + [1., 1., 1.]], + [[1., 1., 1.], + [1., 1., 1.]], + [[1., 1., 1.], + [1., 1., 1.]], + [[1., 1., 1.], + [1., 1., 1.]]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0.15789001, -0.01500003, 0.57889998, -1.15799987], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [ + anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2 + ]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_empty_groundtruth(self): + + def graph_fn(anchor_means, groundtruth_box_corners, gt_class_targets): + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + gt_box_batch = [groundtruth_boxlist] + gt_class_targets_batch = [gt_class_targets] + anchors_boxlist = box_list.BoxList(anchor_means) + + multiclass_target_assigner = self._get_target_assigner() + num_classes = 3 + unmatched_class_label = tf.constant([1] + num_classes * [0], tf.float32) + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_targets( + multiclass_target_assigner, anchors_boxlist, + gt_box_batch, gt_class_targets_batch, unmatched_class_label) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_box_corners = np.zeros((0, 4), dtype=np.float32) + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1]], dtype=np.float32) + exp_cls_targets = [[[1, 0, 0, 0], + [1, 0, 0, 0]]] + exp_cls_weights = [[[1, 1, 1, 1], + [1, 1, 1, 1]]] + exp_reg_targets = [[[0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_reg_weights = [[0, 0]] + num_classes = 3 + pad = 1 + gt_class_targets = np.zeros((0, num_classes + pad), dtype=np.float32) + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_box_corners, gt_class_targets]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + +class BatchGetTargetsTest(test_case.TestCase): + + def test_scalar_targets(self): + batch_match = np.array([[1, 0, 1], + [-2, -1, 1]], dtype=np.int32) + groundtruth_tensors_list = np.array([[11, 12], [13, 14]], dtype=np.int32) + groundtruth_weights_list = np.array([[1.0, 1.0], [1.0, 0.5]], + dtype=np.float32) + unmatched_value = np.array(99, dtype=np.int32) + unmatched_weight = np.array(0.0, dtype=np.float32) + + def graph_fn(batch_match, groundtruth_tensors_list, + groundtruth_weights_list, unmatched_value, unmatched_weight): + targets, weights = targetassigner.batch_get_targets( + batch_match, tf.unstack(groundtruth_tensors_list), + tf.unstack(groundtruth_weights_list), + unmatched_value, unmatched_weight) + return (targets, weights) + + (targets_np, weights_np) = self.execute(graph_fn, [ + batch_match, groundtruth_tensors_list, groundtruth_weights_list, + unmatched_value, unmatched_weight + ]) + self.assertAllEqual([[12, 11, 12], + [99, 99, 14]], targets_np) + self.assertAllClose([[1.0, 1.0, 1.0], + [0.0, 0.0, 0.5]], weights_np) + + def test_1d_targets(self): + batch_match = np.array([[1, 0, 1], + [-2, -1, 1]], dtype=np.int32) + groundtruth_tensors_list = np.array([[[11, 12], [12, 13]], + [[13, 14], [14, 15]]], + dtype=np.float32) + groundtruth_weights_list = np.array([[1.0, 1.0], [1.0, 0.5]], + dtype=np.float32) + unmatched_value = np.array([99, 99], dtype=np.float32) + unmatched_weight = np.array(0.0, dtype=np.float32) + + def graph_fn(batch_match, groundtruth_tensors_list, + groundtruth_weights_list, unmatched_value, unmatched_weight): + targets, weights = targetassigner.batch_get_targets( + batch_match, tf.unstack(groundtruth_tensors_list), + tf.unstack(groundtruth_weights_list), + unmatched_value, unmatched_weight) + return (targets, weights) + + (targets_np, weights_np) = self.execute(graph_fn, [ + batch_match, groundtruth_tensors_list, groundtruth_weights_list, + unmatched_value, unmatched_weight + ]) + self.assertAllClose([[[12, 13], [11, 12], [12, 13]], + [[99, 99], [99, 99], [14, 15]]], targets_np) + self.assertAllClose([[1.0, 1.0, 1.0], + [0.0, 0.0, 0.5]], weights_np) + + +class BatchTargetAssignConfidencesTest(test_case.TestCase): + + def _get_target_assigner(self): + similarity_calc = region_similarity_calculator.IouSimilarity() + matcher = argmax_matcher.ArgMaxMatcher(matched_threshold=0.5, + unmatched_threshold=0.5) + box_coder = mean_stddev_box_coder.MeanStddevBoxCoder(stddev=0.1) + return targetassigner.TargetAssigner(similarity_calc, matcher, box_coder) + + def test_batch_assign_empty_groundtruth(self): + + def graph_fn(anchor_means, groundtruth_box_corners, gt_class_confidences): + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + gt_box_batch = [groundtruth_boxlist] + gt_class_confidences_batch = [gt_class_confidences] + anchors_boxlist = box_list.BoxList(anchor_means) + + num_classes = 3 + implicit_class_weight = 0.5 + unmatched_class_label = tf.constant([1] + num_classes * [0], tf.float32) + multiclass_target_assigner = self._get_target_assigner() + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_confidences( + multiclass_target_assigner, + anchors_boxlist, + gt_box_batch, + gt_class_confidences_batch, + unmatched_class_label=unmatched_class_label, + include_background_class=True, + implicit_class_weight=implicit_class_weight) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_box_corners = np.zeros((0, 4), dtype=np.float32) + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1]], dtype=np.float32) + num_classes = 3 + pad = 1 + gt_class_confidences = np.zeros((0, num_classes + pad), dtype=np.float32) + + exp_cls_targets = [[[1, 0, 0, 0], + [1, 0, 0, 0]]] + exp_cls_weights = [[[0.5, 0.5, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5]]] + exp_reg_targets = [[[0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_reg_weights = [[0, 0]] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, + [anchor_means, groundtruth_box_corners, gt_class_confidences]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_confidences_agnostic(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_confidences_batch = [None, None] + anchors_boxlist = box_list.BoxList(anchor_means) + agnostic_target_assigner = self._get_target_assigner() + implicit_class_weight = 0.5 + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_confidences( + agnostic_target_assigner, + anchors_boxlist, + gt_box_batch, + gt_class_confidences_batch, + include_background_class=False, + implicit_class_weight=implicit_class_weight) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2]], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]], + dtype=np.float32) + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + + exp_cls_targets = [[[1], [0], [0], [0]], + [[0], [1], [1], [0]]] + exp_cls_weights = [[[1], [0.5], [0.5], [0.5]], + [[0.5], [1], [1], [0.5]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0.15789001, -0.01500003, 0.57889998, -1.15799987], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 1, 0]] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute( + graph_fn, [anchor_means, groundtruth_boxlist1, groundtruth_boxlist2]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_confidences_multiclass(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_confidences_batch = [class_targets1, class_targets2] + anchors_boxlist = box_list.BoxList(anchor_means) + multiclass_target_assigner = self._get_target_assigner() + num_classes = 3 + implicit_class_weight = 0.5 + unmatched_class_label = tf.constant([1] + num_classes * [0], tf.float32) + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_confidences( + multiclass_target_assigner, + anchors_boxlist, + gt_box_batch, + gt_class_confidences_batch, + unmatched_class_label=unmatched_class_label, + include_background_class=True, + implicit_class_weight=implicit_class_weight) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2]], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]], + dtype=np.float32) + class_targets1 = np.array([[0, 1, 0, 0]], dtype=np.float32) + class_targets2 = np.array([[0, 0, 0, 1], + [0, 0, -1, 0]], dtype=np.float32) + + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + exp_cls_targets = [[[0, 1, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0]], + [[1, 0, 0, 0], + [0, 0, 0, 1], + [1, 0, 0, 0], + [1, 0, 0, 0]]] + exp_cls_weights = [[[1, 1, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5]], + [[0.5, 0.5, 0.5, 0.5], + [1, 0.5, 0.5, 1], + [0.5, 0.5, 1, 0.5], + [0.5, 0.5, 0.5, 0.5]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 0, 0]] + + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [ + anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2 + ]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_confidences_multiclass_with_padded_groundtruth(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2, groundtruth_weights1, + groundtruth_weights2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_confidences_batch = [class_targets1, class_targets2] + gt_weights = [groundtruth_weights1, groundtruth_weights2] + anchors_boxlist = box_list.BoxList(anchor_means) + multiclass_target_assigner = self._get_target_assigner() + num_classes = 3 + unmatched_class_label = tf.constant([1] + num_classes * [0], tf.float32) + implicit_class_weight = 0.5 + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_confidences( + multiclass_target_assigner, + anchors_boxlist, + gt_box_batch, + gt_class_confidences_batch, + gt_weights, + unmatched_class_label=unmatched_class_label, + include_background_class=True, + implicit_class_weight=implicit_class_weight) + + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2], + [0., 0., 0., 0.]], dtype=np.float32) + groundtruth_weights1 = np.array([1, 0], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842], + [0, 0, 0, 0]], + dtype=np.float32) + groundtruth_weights2 = np.array([1, 1, 0], dtype=np.float32) + class_targets1 = np.array([[0, 1, 0, 0], [0, 0, 0, 0]], dtype=np.float32) + class_targets2 = np.array([[0, 0, 0, 1], + [0, 0, -1, 0], + [0, 0, 0, 0]], dtype=np.float32) + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + + exp_cls_targets = [[[0, 1, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0], + [1, 0, 0, 0]], + [[1, 0, 0, 0], + [0, 0, 0, 1], + [1, 0, 0, 0], + [1, 0, 0, 0]]] + exp_cls_weights = [[[1, 1, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5], + [0.5, 0.5, 0.5, 0.5]], + [[0.5, 0.5, 0.5, 0.5], + [1, 0.5, 0.5, 1], + [0.5, 0.5, 1, 0.5], + [0.5, 0.5, 0.5, 0.5]]] + exp_reg_targets = [[[0, 0, -0.5, -0.5], + [0, 0, 0, 0], + [0, 0, 0, 0,], + [0, 0, 0, 0,],], + [[0, 0, 0, 0,], + [0, 0.01231521, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 0, 0, 0], + [0, 1, 0, 0]] + + (cls_targets_out, cls_weights_out, reg_targets_out, + reg_weights_out) = self.execute(graph_fn, [ + anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2, groundtruth_weights1, + groundtruth_weights2 + ]) + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + + def test_batch_assign_confidences_multidimensional(self): + + def graph_fn(anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2): + box_list1 = box_list.BoxList(groundtruth_boxlist1) + box_list2 = box_list.BoxList(groundtruth_boxlist2) + gt_box_batch = [box_list1, box_list2] + gt_class_confidences_batch = [class_targets1, class_targets2] + anchors_boxlist = box_list.BoxList(anchor_means) + multiclass_target_assigner = self._get_target_assigner() + target_dimensions = (2, 3) + unmatched_class_label = tf.constant(np.zeros(target_dimensions), + tf.float32) + implicit_class_weight = 0.5 + (cls_targets, cls_weights, reg_targets, reg_weights, + _) = targetassigner.batch_assign_confidences( + multiclass_target_assigner, + anchors_boxlist, + gt_box_batch, + gt_class_confidences_batch, + unmatched_class_label=unmatched_class_label, + include_background_class=True, + implicit_class_weight=implicit_class_weight) + return (cls_targets, cls_weights, reg_targets, reg_weights) + + groundtruth_boxlist1 = np.array([[0., 0., 0.2, 0.2]], dtype=np.float32) + groundtruth_boxlist2 = np.array([[0, 0.25123152, 1, 1], + [0.015789, 0.0985, 0.55789, 0.3842]], + dtype=np.float32) + class_targets1 = np.array([[0, 1, 0, 0]], dtype=np.float32) + class_targets2 = np.array([[0, 0, 0, 1], + [0, 0, 1, 0]], dtype=np.float32) + class_targets1 = np.array([[[0, 1, 1], + [1, 1, 0]]], dtype=np.float32) + class_targets2 = np.array([[[0, 1, 1], + [1, 1, 0]], + [[0, 0, 1], + [0, 0, 1]]], dtype=np.float32) + + anchor_means = np.array([[0, 0, .25, .25], + [0, .25, 1, 1], + [0, .1, .5, .5], + [.75, .75, 1, 1]], dtype=np.float32) + + with self.assertRaises(ValueError): + _, _, _, _ = self.execute(graph_fn, [ + anchor_means, groundtruth_boxlist1, groundtruth_boxlist2, + class_targets1, class_targets2 + ]) + + +class CreateTargetAssignerTest(test_case.TestCase): + + def test_create_target_assigner(self): + """Tests that named constructor gives working target assigners. + + TODO(rathodv): Make this test more general. + """ + corners = [[0.0, 0.0, 1.0, 1.0]] + groundtruth = box_list.BoxList(tf.constant(corners)) + + priors = box_list.BoxList(tf.constant(corners)) + if tf_version.is_tf1(): + multibox_ta = (targetassigner + .create_target_assigner('Multibox', stage='proposal')) + multibox_ta.assign(priors, groundtruth) + # No tests on output, as that may vary arbitrarily as new target assigners + # are added. As long as it is constructed correctly and runs without errors, + # tests on the individual assigners cover correctness of the assignments. + + anchors = box_list.BoxList(tf.constant(corners)) + faster_rcnn_proposals_ta = (targetassigner + .create_target_assigner('FasterRCNN', + stage='proposal')) + faster_rcnn_proposals_ta.assign(anchors, groundtruth) + + fast_rcnn_ta = (targetassigner + .create_target_assigner('FastRCNN')) + fast_rcnn_ta.assign(anchors, groundtruth) + + faster_rcnn_detection_ta = (targetassigner + .create_target_assigner('FasterRCNN', + stage='detection')) + faster_rcnn_detection_ta.assign(anchors, groundtruth) + + with self.assertRaises(ValueError): + targetassigner.create_target_assigner('InvalidDetector', + stage='invalid_stage') + + +def _array_argmax(array): + return np.unravel_index(np.argmax(array), array.shape) + + +class CenterNetCenterHeatmapTargetAssignerTest(test_case.TestCase): + + def setUp(self): + super(CenterNetCenterHeatmapTargetAssignerTest, self).setUp() + + self._box_center = [0.0, 0.0, 1.0, 1.0] + self._box_center_small = [0.25, 0.25, 0.75, 0.75] + self._box_lower_left = [0.5, 0.0, 1.0, 0.5] + self._box_center_offset = [0.1, 0.05, 1.0, 1.0] + self._box_odd_coordinates = [0.1625, 0.2125, 0.5625, 0.9625] + + def test_center_location(self): + """Test that the centers are at the correct location.""" + def graph_fn(): + box_batch = [tf.constant([self._box_center, self._box_lower_left])] + classes = [ + tf.one_hot([0, 1], depth=4), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner(4) + targets = assigner.assign_center_targets_from_boxes(80, 80, box_batch, + classes) + return targets + targets = self.execute(graph_fn, []) + self.assertEqual((10, 10), _array_argmax(targets[0, :, :, 0])) + self.assertAlmostEqual(1.0, targets[0, 10, 10, 0]) + self.assertEqual((15, 5), _array_argmax(targets[0, :, :, 1])) + self.assertAlmostEqual(1.0, targets[0, 15, 5, 1]) + + def test_center_batch_shape(self): + """Test that the shape of the target for a batch is correct.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center]), + tf.constant([self._box_center_small]), + ] + classes = [ + tf.one_hot([0, 1], depth=4), + tf.one_hot([2], depth=4), + tf.one_hot([3], depth=4), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner(4) + targets = assigner.assign_center_targets_from_boxes(80, 80, box_batch, + classes) + return targets + targets = self.execute(graph_fn, []) + self.assertEqual((3, 20, 20, 4), targets.shape) + + def test_center_overlap_maximum(self): + """Test that when boxes overlap we, are computing the maximum.""" + def graph_fn(): + box_batch = [ + tf.constant([ + self._box_center, self._box_center_offset, self._box_center, + self._box_center_offset + ]) + ] + classes = [ + tf.one_hot([0, 0, 1, 2], depth=4), + ] + + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner(4) + targets = assigner.assign_center_targets_from_boxes(80, 80, box_batch, + classes) + return targets + targets = self.execute(graph_fn, []) + class0_targets = targets[0, :, :, 0] + class1_targets = targets[0, :, :, 1] + class2_targets = targets[0, :, :, 2] + np.testing.assert_allclose(class0_targets, + np.maximum(class1_targets, class2_targets)) + + def test_size_blur(self): + """Test that the heatmap of a larger box is more blurred.""" + def graph_fn(): + box_batch = [tf.constant([self._box_center, self._box_center_small])] + + classes = [ + tf.one_hot([0, 1], depth=4), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner(4) + targets = assigner.assign_center_targets_from_boxes(80, 80, box_batch, + classes) + return targets + targets = self.execute(graph_fn, []) + self.assertGreater( + np.count_nonzero(targets[:, :, :, 0]), + np.count_nonzero(targets[:, :, :, 1])) + + def test_weights(self): + """Test that the weights correctly ignore ground truth.""" + def graph1_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center]), + tf.constant([self._box_center_small]), + ] + classes = [ + tf.one_hot([0, 1], depth=4), + tf.one_hot([2], depth=4), + tf.one_hot([3], depth=4), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner(4) + targets = assigner.assign_center_targets_from_boxes(80, 80, box_batch, + classes) + return targets + + targets = self.execute(graph1_fn, []) + self.assertAlmostEqual(1.0, targets[0, :, :, 0].max()) + self.assertAlmostEqual(1.0, targets[0, :, :, 1].max()) + self.assertAlmostEqual(1.0, targets[1, :, :, 2].max()) + self.assertAlmostEqual(1.0, targets[2, :, :, 3].max()) + self.assertAlmostEqual(0.0, targets[0, :, :, [2, 3]].max()) + self.assertAlmostEqual(0.0, targets[1, :, :, [0, 1, 3]].max()) + self.assertAlmostEqual(0.0, targets[2, :, :, :3].max()) + + def graph2_fn(): + weights = [ + tf.constant([0., 1.]), + tf.constant([1.]), + tf.constant([1.]), + ] + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center]), + tf.constant([self._box_center_small]), + ] + classes = [ + tf.one_hot([0, 1], depth=4), + tf.one_hot([2], depth=4), + tf.one_hot([3], depth=4), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner(4) + targets = assigner.assign_center_targets_from_boxes(80, 80, box_batch, + classes, + weights) + return targets + targets = self.execute(graph2_fn, []) + self.assertAlmostEqual(1.0, targets[0, :, :, 1].max()) + self.assertAlmostEqual(1.0, targets[1, :, :, 2].max()) + self.assertAlmostEqual(1.0, targets[2, :, :, 3].max()) + self.assertAlmostEqual(0.0, targets[0, :, :, [0, 2, 3]].max()) + self.assertAlmostEqual(0.0, targets[1, :, :, [0, 1, 3]].max()) + self.assertAlmostEqual(0.0, targets[2, :, :, :3].max()) + + def test_low_overlap(self): + def graph1_fn(): + box_batch = [tf.constant([self._box_center])] + classes = [ + tf.one_hot([0], depth=2), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner( + 4, min_overlap=0.1) + targets_low_overlap = assigner.assign_center_targets_from_boxes( + 80, 80, box_batch, classes) + return targets_low_overlap + targets_low_overlap = self.execute(graph1_fn, []) + self.assertLess(1, np.count_nonzero(targets_low_overlap)) + + def graph2_fn(): + box_batch = [tf.constant([self._box_center])] + classes = [ + tf.one_hot([0], depth=2), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner( + 4, min_overlap=0.6) + targets_medium_overlap = assigner.assign_center_targets_from_boxes( + 80, 80, box_batch, classes) + return targets_medium_overlap + targets_medium_overlap = self.execute(graph2_fn, []) + self.assertLess(1, np.count_nonzero(targets_medium_overlap)) + + def graph3_fn(): + box_batch = [tf.constant([self._box_center])] + classes = [ + tf.one_hot([0], depth=2), + ] + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner( + 4, min_overlap=0.99) + targets_high_overlap = assigner.assign_center_targets_from_boxes( + 80, 80, box_batch, classes) + return targets_high_overlap + + targets_high_overlap = self.execute(graph3_fn, []) + self.assertTrue(np.all(targets_low_overlap >= targets_medium_overlap)) + self.assertTrue(np.all(targets_medium_overlap >= targets_high_overlap)) + + def test_empty_box_list(self): + """Test that an empty box list gives an all 0 heatmap.""" + def graph_fn(): + box_batch = [ + tf.zeros((0, 4), dtype=tf.float32), + ] + + classes = [ + tf.zeros((0, 5), dtype=tf.float32), + ] + + assigner = targetassigner.CenterNetCenterHeatmapTargetAssigner( + 4, min_overlap=0.1) + targets = assigner.assign_center_targets_from_boxes( + 80, 80, box_batch, classes) + return targets + targets = self.execute(graph_fn, []) + np.testing.assert_allclose(targets, 0.) + + +class CenterNetBoxTargetAssignerTest(test_case.TestCase): + + def setUp(self): + super(CenterNetBoxTargetAssignerTest, self).setUp() + self._box_center = [0.0, 0.0, 1.0, 1.0] + self._box_center_small = [0.25, 0.25, 0.75, 0.75] + self._box_lower_left = [0.5, 0.0, 1.0, 0.5] + self._box_center_offset = [0.1, 0.05, 1.0, 1.0] + self._box_odd_coordinates = [0.1625, 0.2125, 0.5625, 0.9625] + + def test_max_distance_for_overlap(self): + """Test that the distance ensures the IoU with random boxes.""" + + # TODO(vighneshb) remove this after the `_smallest_positive_root` + # function if fixed. + self.skipTest(('Skipping test because we are using an incorrect version of' + 'the `max_distance_for_overlap` function to reproduce' + ' results.')) + + rng = np.random.RandomState(0) + n_samples = 100 + + width = rng.uniform(1, 100, size=n_samples) + height = rng.uniform(1, 100, size=n_samples) + min_iou = rng.uniform(0.1, 1.0, size=n_samples) + + def graph_fn(): + max_dist = targetassigner.max_distance_for_overlap(height, width, min_iou) + return max_dist + max_dist = self.execute(graph_fn, []) + xmin1 = np.zeros(n_samples) + ymin1 = np.zeros(n_samples) + xmax1 = np.zeros(n_samples) + width + ymax1 = np.zeros(n_samples) + height + + xmin2 = max_dist * np.cos(rng.uniform(0, 2 * np.pi)) + ymin2 = max_dist * np.sin(rng.uniform(0, 2 * np.pi)) + xmax2 = width + max_dist * np.cos(rng.uniform(0, 2 * np.pi)) + ymax2 = height + max_dist * np.sin(rng.uniform(0, 2 * np.pi)) + + boxes1 = np.vstack([ymin1, xmin1, ymax1, xmax1]).T + boxes2 = np.vstack([ymin2, xmin2, ymax2, xmax2]).T + + iou = np.diag(np_box_ops.iou(boxes1, boxes2)) + + self.assertTrue(np.all(iou >= min_iou)) + + def test_max_distance_for_overlap_centernet(self): + """Test the version of the function used in the CenterNet paper.""" + + def graph_fn(): + distance = targetassigner.max_distance_for_overlap(10, 5, 0.5) + return distance + distance = self.execute(graph_fn, []) + self.assertAlmostEqual(2.807764064, distance) + + def test_assign_size_and_offset_targets(self): + """Test the assign_size_and_offset_targets function.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center_offset]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + + assigner = targetassigner.CenterNetBoxTargetAssigner(4) + indices, hw, yx_offset, weights = assigner.assign_size_and_offset_targets( + 80, 80, box_batch) + return indices, hw, yx_offset, weights + indices, hw, yx_offset, weights = self.execute(graph_fn, []) + self.assertEqual(indices.shape, (5, 3)) + self.assertEqual(hw.shape, (5, 2)) + self.assertEqual(yx_offset.shape, (5, 2)) + self.assertEqual(weights.shape, (5,)) + np.testing.assert_array_equal( + indices, + [[0, 10, 10], [0, 15, 5], [1, 11, 10], [2, 10, 10], [2, 7, 11]]) + np.testing.assert_array_equal( + hw, [[20, 20], [10, 10], [18, 19], [10, 10], [8, 15]]) + np.testing.assert_array_equal( + yx_offset, [[0, 0], [0, 0], [0, 0.5], [0, 0], [0.25, 0.75]]) + np.testing.assert_array_equal(weights, 1) + + def test_assign_size_and_offset_targets_weights(self): + """Test the assign_size_and_offset_targets function with box weights.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_lower_left, self._box_center_small]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + + cn_assigner = targetassigner.CenterNetBoxTargetAssigner(4) + weights_batch = [ + tf.constant([0.0, 1.0]), + tf.constant([1.0, 1.0]), + tf.constant([0.0, 0.0]) + ] + indices, hw, yx_offset, weights = cn_assigner.assign_size_and_offset_targets( + 80, 80, box_batch, weights_batch) + return indices, hw, yx_offset, weights + indices, hw, yx_offset, weights = self.execute(graph_fn, []) + self.assertEqual(indices.shape, (6, 3)) + self.assertEqual(hw.shape, (6, 2)) + self.assertEqual(yx_offset.shape, (6, 2)) + self.assertEqual(weights.shape, (6,)) + np.testing.assert_array_equal(indices, + [[0, 10, 10], [0, 15, 5], [1, 15, 5], + [1, 10, 10], [2, 10, 10], [2, 7, 11]]) + np.testing.assert_array_equal( + hw, [[20, 20], [10, 10], [10, 10], [10, 10], [10, 10], [8, 15]]) + np.testing.assert_array_equal( + yx_offset, [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0.25, 0.75]]) + np.testing.assert_array_equal(weights, [0, 1, 1, 1, 0, 0]) + + def test_get_batch_predictions_from_indices(self): + """Test the get_batch_predictions_from_indices function. + + This test verifies that the indices returned by + assign_size_and_offset_targets function work as expected with a predicted + tensor. + + """ + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + + pred_array = np.ones((2, 40, 20, 2), dtype=np.int32) * -1000 + pred_array[0, 20, 10] = [1, 2] + pred_array[0, 30, 5] = [3, 4] + pred_array[1, 20, 10] = [5, 6] + pred_array[1, 14, 11] = [7, 8] + + pred_tensor = tf.constant(pred_array) + + cn_assigner = targetassigner.CenterNetBoxTargetAssigner(4) + indices, _, _, _ = cn_assigner.assign_size_and_offset_targets( + 160, 80, box_batch) + + preds = targetassigner.get_batch_predictions_from_indices( + pred_tensor, indices) + return preds + preds = self.execute(graph_fn, []) + np.testing.assert_array_equal(preds, [[1, 2], [3, 4], [5, 6], [7, 8]]) + + +class CenterNetKeypointTargetAssignerTest(test_case.TestCase): + + def test_keypoint_heatmap_targets(self): + def graph_fn(): + gt_classes_list = [ + tf.one_hot([0, 1, 0, 1], depth=4), + ] + coordinates = tf.expand_dims( + tf.constant( + np.array([[0.1, 0.2, 0.3, 0.4, 0.5], + [float('nan'), 0.7, float('nan'), 0.9, 1.0], + [0.4, 0.1, 0.4, 0.2, 0.1], + [float('nan'), 0.1, 0.5, 0.7, 0.6]]), + dtype=tf.float32), + axis=2) + gt_keypoints_list = [tf.concat([coordinates, coordinates], axis=2)] + gt_boxes_list = [ + tf.constant( + np.array([[0.0, 0.0, 0.3, 0.3], + [0.0, 0.0, 0.5, 0.5], + [0.0, 0.0, 0.5, 0.5], + [0.0, 0.0, 1.0, 1.0]]), + dtype=tf.float32) + ] + + cn_assigner = targetassigner.CenterNetKeypointTargetAssigner( + stride=4, + class_id=1, + keypoint_indices=[0, 2]) + (targets, num_instances_batch, + valid_mask) = cn_assigner.assign_keypoint_heatmap_targets( + 120, + 80, + gt_keypoints_list, + gt_classes_list, + gt_boxes_list=gt_boxes_list) + return targets, num_instances_batch, valid_mask + + targets, num_instances_batch, valid_mask = self.execute(graph_fn, []) + # keypoint (0.5, 0.5) is selected. The peak is expected to appear at the + # center of the image. + self.assertEqual((15, 10), _array_argmax(targets[0, :, :, 1])) + self.assertAlmostEqual(1.0, targets[0, 15, 10, 1]) + # No peak for the first class since NaN is selected. + self.assertAlmostEqual(0.0, targets[0, 15, 10, 0]) + # Verify the output heatmap shape. + self.assertAllEqual([1, 30, 20, 2], targets.shape) + # Verify the number of instances is correct. + np.testing.assert_array_almost_equal([[0, 1]], + num_instances_batch) + # When calling the function, we specify the class id to be 1 (1th and 3rd) + # instance and the keypoint indices to be [0, 2], meaning that the 1st + # instance is the target class with no valid keypoints in it. As a result, + # the region of the 1st instance boxing box should be blacked out + # (0.0, 0.0, 0.5, 0.5), transfering to (0, 0, 15, 10) in absolute output + # space. + self.assertAlmostEqual(np.sum(valid_mask[:, 0:16, 0:11]), 0.0) + # All other values are 1.0 so the sum is: 30 * 20 - 16 * 11 = 424. + self.assertAlmostEqual(np.sum(valid_mask), 424.0) + + def test_assign_keypoints_offset_targets(self): + def graph_fn(): + gt_classes_list = [ + tf.one_hot([0, 1, 0, 1], depth=4), + ] + coordinates = tf.expand_dims( + tf.constant( + np.array([[0.1, 0.2, 0.3, 0.4, 0.5], + [float('nan'), 0.7, float('nan'), 0.9, 0.4], + [0.4, 0.1, 0.4, 0.2, 0.0], + [float('nan'), 0.0, 0.12, 0.7, 0.4]]), + dtype=tf.float32), + axis=2) + gt_keypoints_list = [tf.concat([coordinates, coordinates], axis=2)] + + cn_assigner = targetassigner.CenterNetKeypointTargetAssigner( + stride=4, + class_id=1, + keypoint_indices=[0, 2]) + (indices, offsets, weights) = cn_assigner.assign_keypoints_offset_targets( + height=120, + width=80, + gt_keypoints_list=gt_keypoints_list, + gt_classes_list=gt_classes_list) + return indices, weights, offsets + indices, weights, offsets = self.execute(graph_fn, []) + # Only the last element has positive weight. + np.testing.assert_array_almost_equal( + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], weights) + # Validate the last element's indices and offsets. + np.testing.assert_array_equal([0, 3, 2], indices[7, :]) + np.testing.assert_array_almost_equal([0.6, 0.4], offsets[7, :]) + + def test_assign_keypoints_offset_targets_radius(self): + def graph_fn(): + gt_classes_list = [ + tf.one_hot([0, 1, 0, 1], depth=4), + ] + coordinates = tf.expand_dims( + tf.constant( + np.array([[0.1, 0.2, 0.3, 0.4, 0.5], + [float('nan'), 0.7, float('nan'), 0.9, 0.4], + [0.4, 0.1, 0.4, 0.2, 0.0], + [float('nan'), 0.0, 0.12, 0.7, 0.4]]), + dtype=tf.float32), + axis=2) + gt_keypoints_list = [tf.concat([coordinates, coordinates], axis=2)] + + cn_assigner = targetassigner.CenterNetKeypointTargetAssigner( + stride=4, + class_id=1, + keypoint_indices=[0, 2], + peak_radius=1, + per_keypoint_offset=True) + (indices, offsets, weights) = cn_assigner.assign_keypoints_offset_targets( + height=120, + width=80, + gt_keypoints_list=gt_keypoints_list, + gt_classes_list=gt_classes_list) + return indices, weights, offsets + indices, weights, offsets = self.execute(graph_fn, []) + + # There are total 8 * 5 (neighbors) = 40 targets. + self.assertAllEqual(indices.shape, [40, 4]) + self.assertAllEqual(offsets.shape, [40, 2]) + self.assertAllEqual(weights.shape, [40]) + # Only the last 5 (radius 1 generates 5 valid points) element has positive + # weight. + np.testing.assert_array_almost_equal([ + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 + ], weights) + # Validate the last element's (with neighbors) indices and offsets. + np.testing.assert_array_equal([0, 2, 2, 1], indices[35, :]) + np.testing.assert_array_equal([0, 3, 1, 1], indices[36, :]) + np.testing.assert_array_equal([0, 3, 2, 1], indices[37, :]) + np.testing.assert_array_equal([0, 3, 3, 1], indices[38, :]) + np.testing.assert_array_equal([0, 4, 2, 1], indices[39, :]) + np.testing.assert_array_almost_equal([1.6, 0.4], offsets[35, :]) + np.testing.assert_array_almost_equal([0.6, 1.4], offsets[36, :]) + np.testing.assert_array_almost_equal([0.6, 0.4], offsets[37, :]) + np.testing.assert_array_almost_equal([0.6, -0.6], offsets[38, :]) + np.testing.assert_array_almost_equal([-0.4, 0.4], offsets[39, :]) + + def test_assign_joint_regression_targets(self): + def graph_fn(): + gt_boxes_list = [ + tf.constant( + np.array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 1.0]]), + dtype=tf.float32) + ] + gt_classes_list = [ + tf.one_hot([0, 1, 0, 1], depth=4), + ] + coordinates = tf.expand_dims( + tf.constant( + np.array([[0.1, 0.2, 0.3, 0.4, 0.5], + [float('nan'), 0.7, float('nan'), 0.9, 0.4], + [0.4, 0.1, 0.4, 0.2, 0.0], + [float('nan'), 0.0, 0.12, 0.7, 0.4]]), + dtype=tf.float32), + axis=2) + gt_keypoints_list = [tf.concat([coordinates, coordinates], axis=2)] + + cn_assigner = targetassigner.CenterNetKeypointTargetAssigner( + stride=4, + class_id=1, + keypoint_indices=[0, 2]) + (indices, offsets, weights) = cn_assigner.assign_joint_regression_targets( + height=120, + width=80, + gt_keypoints_list=gt_keypoints_list, + gt_classes_list=gt_classes_list, + gt_boxes_list=gt_boxes_list) + return indices, offsets, weights + indices, offsets, weights = self.execute(graph_fn, []) + np.testing.assert_array_almost_equal( + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0], weights) + np.testing.assert_array_equal([0, 15, 10, 1], indices[7, :]) + np.testing.assert_array_almost_equal([-11.4, -7.6], offsets[7, :]) + + def test_assign_joint_regression_targets_radius(self): + def graph_fn(): + gt_boxes_list = [ + tf.constant( + np.array([[0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 1.0, 1.0]]), + dtype=tf.float32) + ] + gt_classes_list = [ + tf.one_hot([0, 1, 0, 1], depth=4), + ] + coordinates = tf.expand_dims( + tf.constant( + np.array([[0.1, 0.2, 0.3, 0.4, 0.5], + [float('nan'), 0.7, float('nan'), 0.9, 0.4], + [0.4, 0.1, 0.4, 0.2, 0.0], + [float('nan'), 0.0, 0.12, 0.7, 0.4]]), + dtype=tf.float32), + axis=2) + gt_keypoints_list = [tf.concat([coordinates, coordinates], axis=2)] + + cn_assigner = targetassigner.CenterNetKeypointTargetAssigner( + stride=4, + class_id=1, + keypoint_indices=[0, 2], + peak_radius=1) + (indices, offsets, weights) = cn_assigner.assign_joint_regression_targets( + height=120, + width=80, + gt_keypoints_list=gt_keypoints_list, + gt_classes_list=gt_classes_list, + gt_boxes_list=gt_boxes_list) + return indices, offsets, weights + indices, offsets, weights = self.execute(graph_fn, []) + + # There are total 8 * 5 (neighbors) = 40 targets. + self.assertAllEqual(indices.shape, [40, 4]) + self.assertAllEqual(offsets.shape, [40, 2]) + self.assertAllEqual(weights.shape, [40]) + # Only the last 5 (radius 1 generates 5 valid points) element has positive + # weight. + np.testing.assert_array_almost_equal([ + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0 + ], weights) + # Test the values of the indices and offsets of the last 5 elements. + np.testing.assert_array_equal([0, 14, 10, 1], indices[35, :]) + np.testing.assert_array_equal([0, 15, 9, 1], indices[36, :]) + np.testing.assert_array_equal([0, 15, 10, 1], indices[37, :]) + np.testing.assert_array_equal([0, 15, 11, 1], indices[38, :]) + np.testing.assert_array_equal([0, 16, 10, 1], indices[39, :]) + np.testing.assert_array_almost_equal([-10.4, -7.6], offsets[35, :]) + np.testing.assert_array_almost_equal([-11.4, -6.6], offsets[36, :]) + np.testing.assert_array_almost_equal([-11.4, -7.6], offsets[37, :]) + np.testing.assert_array_almost_equal([-11.4, -8.6], offsets[38, :]) + np.testing.assert_array_almost_equal([-12.4, -7.6], offsets[39, :]) + + +class CenterNetMaskTargetAssignerTest(test_case.TestCase): + + def test_assign_segmentation_targets(self): + def graph_fn(): + gt_masks_list = [ + # Example 0. + tf.constant([ + [ + [1., 0., 0., 0.], + [1., 1., 0., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.], + ], + [ + [0., 0., 0., 0.], + [0., 0., 0., 1.], + [0., 0., 0., 0.], + [0., 0., 0., 0.], + ], + [ + [1., 1., 0., 0.], + [1., 1., 0., 0.], + [0., 0., 1., 1.], + [0., 0., 1., 1.], + ] + ], dtype=tf.float32), + # Example 1. + tf.constant([ + [ + [1., 1., 0., 1.], + [1., 1., 1., 1.], + [0., 0., 1., 1.], + [0., 0., 0., 1.], + ], + [ + [0., 0., 0., 0.], + [0., 0., 0., 0.], + [1., 1., 0., 0.], + [1., 1., 0., 0.], + ], + ], dtype=tf.float32), + ] + gt_classes_list = [ + # Example 0. + tf.constant([[1., 0., 0.], + [0., 1., 0.], + [1., 0., 0.]], dtype=tf.float32), + # Example 1. + tf.constant([[0., 1., 0.], + [0., 1., 0.]], dtype=tf.float32) + ] + cn_assigner = targetassigner.CenterNetMaskTargetAssigner(stride=2) + segmentation_target = cn_assigner.assign_segmentation_targets( + gt_masks_list=gt_masks_list, + gt_classes_list=gt_classes_list, + mask_resize_method=targetassigner.ResizeMethod.NEAREST_NEIGHBOR) + return segmentation_target + segmentation_target = self.execute(graph_fn, []) + + expected_seg_target = np.array([ + # Example 0 [[class 0, class 1], [background, class 0]] + [[[1, 0, 0], [0, 1, 0]], + [[0, 0, 0], [1, 0, 0]]], + # Example 1 [[class 1, class 1], [class 1, class 1]] + [[[0, 1, 0], [0, 1, 0]], + [[0, 1, 0], [0, 1, 0]]], + ], dtype=np.float32) + np.testing.assert_array_almost_equal( + expected_seg_target, segmentation_target) + + def test_assign_segmentation_targets_no_objects(self): + def graph_fn(): + gt_masks_list = [tf.zeros((0, 5, 5))] + gt_classes_list = [tf.zeros((0, 10))] + cn_assigner = targetassigner.CenterNetMaskTargetAssigner(stride=1) + segmentation_target = cn_assigner.assign_segmentation_targets( + gt_masks_list=gt_masks_list, + gt_classes_list=gt_classes_list, + mask_resize_method=targetassigner.ResizeMethod.NEAREST_NEIGHBOR) + return segmentation_target + + segmentation_target = self.execute(graph_fn, []) + expected_seg_target = np.zeros((1, 5, 5, 10)) + np.testing.assert_array_almost_equal( + expected_seg_target, segmentation_target) + + +class CenterNetDensePoseTargetAssignerTest(test_case.TestCase): + + def test_assign_part_and_coordinate_targets(self): + def graph_fn(): + gt_dp_num_points_list = [ + # Example 0. + tf.constant([2, 0, 3], dtype=tf.int32), + # Example 1. + tf.constant([1, 1], dtype=tf.int32), + ] + gt_dp_part_ids_list = [ + # Example 0. + tf.constant([[1, 6, 0], + [0, 0, 0], + [0, 2, 3]], dtype=tf.int32), + # Example 1. + tf.constant([[7, 0, 0], + [0, 0, 0]], dtype=tf.int32), + ] + gt_dp_surface_coords_list = [ + # Example 0. + tf.constant( + [[[0.11, 0.2, 0.3, 0.4], # Box 0. + [0.6, 0.4, 0.1, 0.0], + [0.0, 0.0, 0.0, 0.0]], + [[0.0, 0.0, 0.0, 0.0], # Box 1. + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], + [[0.22, 0.1, 0.6, 0.8], # Box 2. + [0.0, 0.4, 0.5, 1.0], + [0.3, 0.2, 0.4, 0.1]]], + dtype=tf.float32), + # Example 1. + tf.constant( + [[[0.5, 0.5, 0.3, 1.0], # Box 0. + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]], + [[0.2, 0.2, 0.5, 0.8], # Box 1. + [0.0, 0.0, 0.0, 0.0], + [0.0, 0.0, 0.0, 0.0]]], + dtype=tf.float32), + ] + gt_weights_list = [ + # Example 0. + tf.constant([1.0, 1.0, 0.5], dtype=tf.float32), + # Example 1. + tf.constant([0.0, 1.0], dtype=tf.float32), + ] + cn_assigner = targetassigner.CenterNetDensePoseTargetAssigner(stride=4) + batch_indices, batch_part_ids, batch_surface_coords, batch_weights = ( + cn_assigner.assign_part_and_coordinate_targets( + height=120, + width=80, + gt_dp_num_points_list=gt_dp_num_points_list, + gt_dp_part_ids_list=gt_dp_part_ids_list, + gt_dp_surface_coords_list=gt_dp_surface_coords_list, + gt_weights_list=gt_weights_list)) + + return batch_indices, batch_part_ids, batch_surface_coords, batch_weights + batch_indices, batch_part_ids, batch_surface_coords, batch_weights = ( + self.execute(graph_fn, [])) + + expected_batch_indices = np.array([ + # Example 0. e.g. + # The first set of indices is calculated as follows: + # floor(0.11*120/4) = 3, floor(0.2*80/4) = 4. + [0, 3, 4, 1], [0, 18, 8, 6], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], + [0, 0, 0, 0], [0, 6, 2, 0], [0, 0, 8, 2], [0, 9, 4, 3], + # Example 1. + [1, 15, 10, 7], [1, 0, 0, 0], [1, 0, 0, 0], [1, 6, 4, 0], [1, 0, 0, 0], + [1, 0, 0, 0] + ], dtype=np.int32) + expected_batch_part_ids = tf.one_hot( + [1, 6, 0, 0, 0, 0, 0, 2, 3, 7, 0, 0, 0, 0, 0], depth=24).numpy() + expected_batch_surface_coords = np.array([ + # Box 0. + [0.3, 0.4], [0.1, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], + [0.6, 0.8], [0.5, 1.0], [0.4, 0.1], + # Box 1. + [0.3, 1.0], [0.0, 0.0], [0.0, 0.0], [0.5, 0.8], [0.0, 0.0], [0.0, 0.0], + ], np.float32) + expected_batch_weights = np.array([ + # Box 0. + 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, + # Box 1. + 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 + ], dtype=np.float32) + self.assertAllEqual(expected_batch_indices, batch_indices) + self.assertAllEqual(expected_batch_part_ids, batch_part_ids) + self.assertAllClose(expected_batch_surface_coords, batch_surface_coords) + self.assertAllClose(expected_batch_weights, batch_weights) + + +class CenterNetTrackTargetAssignerTest(test_case.TestCase): + + def setUp(self): + super(CenterNetTrackTargetAssignerTest, self).setUp() + self._box_center = [0.0, 0.0, 1.0, 1.0] + self._box_center_small = [0.25, 0.25, 0.75, 0.75] + self._box_lower_left = [0.5, 0.0, 1.0, 0.5] + self._box_center_offset = [0.1, 0.05, 1.0, 1.0] + self._box_odd_coordinates = [0.1625, 0.2125, 0.5625, 0.9625] + + def test_assign_track_targets(self): + """Test the assign_track_targets function.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_lower_left, self._box_center_small]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + track_id_batch = [ + tf.constant([0, 1]), + tf.constant([1, 0]), + tf.constant([0, 2]), + ] + + assigner = targetassigner.CenterNetTrackTargetAssigner( + stride=4, num_track_ids=3) + + (batch_indices, batch_weights, + track_targets) = assigner.assign_track_targets( + height=80, + width=80, + gt_track_ids_list=track_id_batch, + gt_boxes_list=box_batch) + return batch_indices, batch_weights, track_targets + + indices, weights, track_ids = self.execute(graph_fn, []) + + self.assertEqual(indices.shape, (3, 2, 3)) + self.assertEqual(track_ids.shape, (3, 2, 3)) + self.assertEqual(weights.shape, (3, 2)) + + np.testing.assert_array_equal(indices, + [[[0, 10, 10], [0, 15, 5]], + [[1, 15, 5], [1, 10, 10]], + [[2, 10, 10], [2, 7, 11]]]) + np.testing.assert_array_equal(track_ids, + [[[1, 0, 0], [0, 1, 0]], + [[0, 1, 0], [1, 0, 0]], + [[1, 0, 0], [0, 0, 1]]]) + np.testing.assert_array_equal(weights, [[1, 1], [1, 1], [1, 1]]) + + def test_assign_track_targets_weights(self): + """Test the assign_track_targets function with box weights.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_lower_left, self._box_center_small]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + track_id_batch = [ + tf.constant([0, 1]), + tf.constant([1, 0]), + tf.constant([0, 2]), + ] + weights_batch = [ + tf.constant([0.0, 1.0]), + tf.constant([1.0, 1.0]), + tf.constant([0.0, 0.0]) + ] + + assigner = targetassigner.CenterNetTrackTargetAssigner( + stride=4, num_track_ids=3) + + (batch_indices, batch_weights, + track_targets) = assigner.assign_track_targets( + height=80, + width=80, + gt_track_ids_list=track_id_batch, + gt_boxes_list=box_batch, + gt_weights_list=weights_batch) + return batch_indices, batch_weights, track_targets + + indices, weights, track_ids = self.execute(graph_fn, []) + + self.assertEqual(indices.shape, (3, 2, 3)) + self.assertEqual(track_ids.shape, (3, 2, 3)) + self.assertEqual(weights.shape, (3, 2)) + + np.testing.assert_array_equal(indices, + [[[0, 10, 10], [0, 15, 5]], + [[1, 15, 5], [1, 10, 10]], + [[2, 10, 10], [2, 7, 11]]]) + np.testing.assert_array_equal(track_ids, + [[[1, 0, 0], [0, 1, 0]], + [[0, 1, 0], [1, 0, 0]], + [[1, 0, 0], [0, 0, 1]]]) + np.testing.assert_array_equal(weights, [[0, 1], [1, 1], [0, 0]]) + # TODO(xwwang): Add a test for the case when no objects are detected. + + +class CornerOffsetTargetAssignerTest(test_case.TestCase): + + def test_filter_overlap_min_area_empty(self): + """Test that empty masks work on CPU.""" + def graph_fn(masks): + return targetassigner.filter_mask_overlap_min_area(masks) + + masks = self.execute_cpu(graph_fn, [np.zeros((0, 5, 5), dtype=np.float32)]) + self.assertEqual(masks.shape, (0, 5, 5)) + + def test_filter_overlap_min_area(self): + """Test the object with min. area is selected instead of overlap.""" + def graph_fn(masks): + return targetassigner.filter_mask_overlap_min_area(masks) + + masks = np.zeros((3, 4, 4), dtype=np.float32) + masks[0, :2, :2] = 1.0 + masks[1, :3, :3] = 1.0 + masks[2, 3, 3] = 1.0 + + masks = self.execute(graph_fn, [masks]) + + self.assertAllClose(masks[0], + [[1, 1, 0, 0], + [1, 1, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0]]) + self.assertAllClose(masks[1], + [[0, 0, 1, 0], + [0, 0, 1, 0], + [1, 1, 1, 0], + [0, 0, 0, 0]]) + + self.assertAllClose(masks[2], + [[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + def test_assign_corner_offset_single_object(self): + """Test that corner offsets are correct with a single object.""" + assigner = targetassigner.CenterNetCornerOffsetTargetAssigner(stride=1) + + def graph_fn(): + boxes = [ + tf.constant([[0., 0., 1., 1.]]) + ] + mask = np.zeros((1, 4, 4), dtype=np.float32) + mask[0, 1:3, 1:3] = 1.0 + + masks = [tf.constant(mask)] + return assigner.assign_corner_offset_targets(boxes, masks) + + corner_offsets, foreground = self.execute(graph_fn, []) + self.assertAllClose(foreground[0], + [[0, 0, 0, 0], + [0, 1, 1, 0], + [0, 1, 1, 0], + [0, 0, 0, 0]]) + + self.assertAllClose(corner_offsets[0, :, :, 0], + [[0, 0, 0, 0], + [0, -1, -1, 0], + [0, -2, -2, 0], + [0, 0, 0, 0]]) + self.assertAllClose(corner_offsets[0, :, :, 1], + [[0, 0, 0, 0], + [0, -1, -2, 0], + [0, -1, -2, 0], + [0, 0, 0, 0]]) + self.assertAllClose(corner_offsets[0, :, :, 2], + [[0, 0, 0, 0], + [0, 3, 3, 0], + [0, 2, 2, 0], + [0, 0, 0, 0]]) + self.assertAllClose(corner_offsets[0, :, :, 3], + [[0, 0, 0, 0], + [0, 3, 2, 0], + [0, 3, 2, 0], + [0, 0, 0, 0]]) + + def test_assign_corner_offset_multiple_objects(self): + """Test corner offsets are correct with multiple objects.""" + assigner = targetassigner.CenterNetCornerOffsetTargetAssigner(stride=1) + + def graph_fn(): + boxes = [ + tf.constant([[0., 0., 1., 1.], [0., 0., 0., 0.]]), + tf.constant([[0., 0., .25, .25], [.25, .25, 1., 1.]]) + ] + mask1 = np.zeros((2, 4, 4), dtype=np.float32) + mask1[0, 0, 0] = 1.0 + mask1[0, 3, 3] = 1.0 + + mask2 = np.zeros((2, 4, 4), dtype=np.float32) + mask2[0, :2, :2] = 1.0 + mask2[1, 1:, 1:] = 1.0 + + masks = [tf.constant(mask1), tf.constant(mask2)] + return assigner.assign_corner_offset_targets(boxes, masks) + + corner_offsets, foreground = self.execute(graph_fn, []) + self.assertEqual(corner_offsets.shape, (2, 4, 4, 4)) + self.assertEqual(foreground.shape, (2, 4, 4)) + + self.assertAllClose(foreground[0], + [[1, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + self.assertAllClose(corner_offsets[0, :, :, 0], + [[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, -3]]) + self.assertAllClose(corner_offsets[0, :, :, 1], + [[0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, -3]]) + self.assertAllClose(corner_offsets[0, :, :, 2], + [[4, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + self.assertAllClose(corner_offsets[0, :, :, 3], + [[4, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1]]) + + self.assertAllClose(foreground[1], + [[1, 1, 0, 0], + [1, 1, 1, 1], + [0, 1, 1, 1], + [0, 1, 1, 1]]) + + self.assertAllClose(corner_offsets[1, :, :, 0], + [[0, 0, 0, 0], + [-1, -1, 0, 0], + [0, -1, -1, -1], + [0, -2, -2, -2]]) + self.assertAllClose(corner_offsets[1, :, :, 1], + [[0, -1, 0, 0], + [0, -1, -1, -2], + [0, 0, -1, -2], + [0, 0, -1, -2]]) + self.assertAllClose(corner_offsets[1, :, :, 2], + [[1, 1, 0, 0], + [0, 0, 3, 3], + [0, 2, 2, 2], + [0, 1, 1, 1]]) + self.assertAllClose(corner_offsets[1, :, :, 3], + [[1, 0, 0, 0], + [1, 0, 2, 1], + [0, 3, 2, 1], + [0, 3, 2, 1]]) + + def test_assign_corner_offsets_no_objects(self): + """Test assignment works with empty input on cpu.""" + assigner = targetassigner.CenterNetCornerOffsetTargetAssigner(stride=1) + + def graph_fn(): + boxes = [ + tf.zeros((0, 4), dtype=tf.float32) + ] + masks = [tf.zeros((0, 5, 5), dtype=tf.float32)] + return assigner.assign_corner_offset_targets(boxes, masks) + + corner_offsets, foreground = self.execute_cpu(graph_fn, []) + self.assertAllClose(corner_offsets, np.zeros((1, 5, 5, 4))) + self.assertAllClose(foreground, np.zeros((1, 5, 5))) + + +class CenterNetTemporalOffsetTargetAssigner(test_case.TestCase): + + def setUp(self): + super(CenterNetTemporalOffsetTargetAssigner, self).setUp() + self._box_center = [0.0, 0.0, 1.0, 1.0] + self._box_center_small = [0.25, 0.25, 0.75, 0.75] + self._box_lower_left = [0.5, 0.0, 1.0, 0.5] + self._box_center_offset = [0.1, 0.05, 1.0, 1.0] + self._box_odd_coordinates = [0.1625, 0.2125, 0.5625, 0.9625] + self._offset_center = [0.5, 0.4] + self._offset_center_small = [0.1, 0.1] + self._offset_lower_left = [-0.1, 0.1] + self._offset_center_offset = [0.4, 0.3] + self._offset_odd_coord = [0.125, -0.125] + + def test_assign_empty_groundtruths(self): + """Tests the assign_offset_targets function with empty inputs.""" + def graph_fn(): + box_batch = [ + tf.zeros((0, 4), dtype=tf.float32), + ] + + offset_batch = [ + tf.zeros((0, 2), dtype=tf.float32), + ] + + match_flag_batch = [ + tf.zeros((0), dtype=tf.float32), + ] + + assigner = targetassigner.CenterNetTemporalOffsetTargetAssigner(4) + indices, temporal_offset, weights = assigner.assign_temporal_offset_targets( + 80, 80, box_batch, offset_batch, match_flag_batch) + return indices, temporal_offset, weights + indices, temporal_offset, weights = self.execute(graph_fn, []) + self.assertEqual(indices.shape, (0, 3)) + self.assertEqual(temporal_offset.shape, (0, 2)) + self.assertEqual(weights.shape, (0,)) + + def test_assign_offset_targets(self): + """Tests the assign_offset_targets function.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center_offset]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + + offset_batch = [ + tf.constant([self._offset_center, self._offset_lower_left]), + tf.constant([self._offset_center_offset]), + tf.constant([self._offset_center_small, self._offset_odd_coord]), + ] + + match_flag_batch = [ + tf.constant([1.0, 1.0]), + tf.constant([1.0]), + tf.constant([1.0, 1.0]), + ] + + assigner = targetassigner.CenterNetTemporalOffsetTargetAssigner(4) + indices, temporal_offset, weights = assigner.assign_temporal_offset_targets( + 80, 80, box_batch, offset_batch, match_flag_batch) + return indices, temporal_offset, weights + indices, temporal_offset, weights = self.execute(graph_fn, []) + self.assertEqual(indices.shape, (5, 3)) + self.assertEqual(temporal_offset.shape, (5, 2)) + self.assertEqual(weights.shape, (5,)) + np.testing.assert_array_equal( + indices, + [[0, 10, 10], [0, 15, 5], [1, 11, 10], [2, 10, 10], [2, 7, 11]]) + np.testing.assert_array_almost_equal( + temporal_offset, + [[0.5, 0.4], [-0.1, 0.1], [0.4, 0.3], [0.1, 0.1], [0.125, -0.125]]) + np.testing.assert_array_equal(weights, 1) + + def test_assign_offset_targets_with_match_flags(self): + """Tests the assign_offset_targets function with match flags.""" + def graph_fn(): + box_batch = [ + tf.constant([self._box_center, self._box_lower_left]), + tf.constant([self._box_center_offset]), + tf.constant([self._box_center_small, self._box_odd_coordinates]), + ] + + offset_batch = [ + tf.constant([self._offset_center, self._offset_lower_left]), + tf.constant([self._offset_center_offset]), + tf.constant([self._offset_center_small, self._offset_odd_coord]), + ] + + match_flag_batch = [ + tf.constant([0.0, 1.0]), + tf.constant([1.0]), + tf.constant([1.0, 1.0]), + ] + + cn_assigner = targetassigner.CenterNetTemporalOffsetTargetAssigner(4) + weights_batch = [ + tf.constant([1.0, 0.0]), + tf.constant([1.0]), + tf.constant([1.0, 1.0]) + ] + indices, temporal_offset, weights = cn_assigner.assign_temporal_offset_targets( + 80, 80, box_batch, offset_batch, match_flag_batch, weights_batch) + return indices, temporal_offset, weights + indices, temporal_offset, weights = self.execute(graph_fn, []) + self.assertEqual(indices.shape, (5, 3)) + self.assertEqual(temporal_offset.shape, (5, 2)) + self.assertEqual(weights.shape, (5,)) + + np.testing.assert_array_equal( + indices, + [[0, 10, 10], [0, 15, 5], [1, 11, 10], [2, 10, 10], [2, 7, 11]]) + np.testing.assert_array_almost_equal( + temporal_offset, + [[0.5, 0.4], [-0.1, 0.1], [0.4, 0.3], [0.1, 0.1], [0.125, -0.125]]) + np.testing.assert_array_equal(weights, [0, 0, 1, 1, 1]) + + +class DETRTargetAssignerTest(test_case.TestCase): + + def test_assign_detr(self): + def graph_fn(pred_corners, groundtruth_box_corners, + groundtruth_labels, predicted_labels): + detr_target_assigner = targetassigner.DETRTargetAssigner() + pred_boxlist = box_list.BoxList(pred_corners) + groundtruth_boxlist = box_list.BoxList(groundtruth_box_corners) + result = detr_target_assigner.assign( + pred_boxlist, groundtruth_boxlist, + predicted_labels, groundtruth_labels) + (cls_targets, cls_weights, reg_targets, reg_weights) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + pred_corners = np.array([[0.25, 0.25, 0.4, 0.2], + [0.5, 0.8, 1.0, 0.8], + [0.9, 0.5, 0.1, 1.0]], dtype=np.float32) + groundtruth_box_corners = np.array([[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9]], + dtype=np.float32) + predicted_labels = np.array([[-3.0, 3.0], [2.0, 9.4], [5.0, 1.0]], + dtype=np.float32) + groundtruth_labels = np.array([[0.0, 1.0], [0.0, 1.0]], + dtype=np.float32) + + exp_cls_targets = [[0, 1], [0, 1], [1, 0]] + exp_cls_weights = [[1, 1], [1, 1], [1, 1]] + exp_reg_targets = [[0.25, 0.25, 0.5, 0.5], + [0.7, 0.7, 0.4, 0.4], + [0, 0, 0, 0]] + exp_reg_weights = [1, 1, 0] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute_cpu( + graph_fn, [pred_corners, groundtruth_box_corners, + groundtruth_labels, predicted_labels]) + + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + def test_batch_assign_detr(self): + def graph_fn(pred_corners, groundtruth_box_corners, + groundtruth_labels, predicted_labels): + detr_target_assigner = targetassigner.DETRTargetAssigner() + result = detr_target_assigner.batch_assign( + pred_corners, groundtruth_box_corners, + [predicted_labels], [groundtruth_labels]) + (cls_targets, cls_weights, reg_targets, reg_weights) = result + return (cls_targets, cls_weights, reg_targets, reg_weights) + + pred_corners = np.array([[[0.25, 0.25, 0.4, 0.2], + [0.5, 0.8, 1.0, 0.8], + [0.9, 0.5, 0.1, 1.0]]], dtype=np.float32) + groundtruth_box_corners = np.array([[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.9, 0.9]]], + dtype=np.float32) + predicted_labels = np.array([[-3.0, 3.0], [2.0, 9.4], [5.0, 1.0]], + dtype=np.float32) + groundtruth_labels = np.array([[0.0, 1.0], [0.0, 1.0]], + dtype=np.float32) + + exp_cls_targets = [[[0, 1], [0, 1], [1, 0]]] + exp_cls_weights = [[[1, 1], [1, 1], [1, 1]]] + exp_reg_targets = [[[0.25, 0.25, 0.5, 0.5], + [0.7, 0.7, 0.4, 0.4], + [0, 0, 0, 0]]] + exp_reg_weights = [[1, 1, 0]] + + (cls_targets_out, + cls_weights_out, reg_targets_out, reg_weights_out) = self.execute_cpu( + graph_fn, [pred_corners, groundtruth_box_corners, + groundtruth_labels, predicted_labels]) + + self.assertAllClose(cls_targets_out, exp_cls_targets) + self.assertAllClose(cls_weights_out, exp_cls_weights) + self.assertAllClose(reg_targets_out, exp_reg_targets) + self.assertAllClose(reg_weights_out, exp_reg_weights) + self.assertEqual(cls_targets_out.dtype, np.float32) + self.assertEqual(cls_weights_out.dtype, np.float32) + self.assertEqual(reg_targets_out.dtype, np.float32) + self.assertEqual(reg_weights_out.dtype, np.float32) + + +if __name__ == '__main__': + tf.enable_v2_behavior() + tf.test.main() diff --git a/workspace/virtuallab/object_detection/data/ava_label_map_v2.1.pbtxt b/workspace/virtuallab/object_detection/data/ava_label_map_v2.1.pbtxt new file mode 100644 index 0000000..5e2c485 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/ava_label_map_v2.1.pbtxt @@ -0,0 +1,240 @@ +item { + name: "bend/bow (at the waist)" + id: 1 +} +item { + name: "crouch/kneel" + id: 3 +} +item { + name: "dance" + id: 4 +} +item { + name: "fall down" + id: 5 +} +item { + name: "get up" + id: 6 +} +item { + name: "jump/leap" + id: 7 +} +item { + name: "lie/sleep" + id: 8 +} +item { + name: "martial art" + id: 9 +} +item { + name: "run/jog" + id: 10 +} +item { + name: "sit" + id: 11 +} +item { + name: "stand" + id: 12 +} +item { + name: "swim" + id: 13 +} +item { + name: "walk" + id: 14 +} +item { + name: "answer phone" + id: 15 +} +item { + name: "carry/hold (an object)" + id: 17 +} +item { + name: "climb (e.g., a mountain)" + id: 20 +} +item { + name: "close (e.g., a door, a box)" + id: 22 +} +item { + name: "cut" + id: 24 +} +item { + name: "dress/put on clothing" + id: 26 +} +item { + name: "drink" + id: 27 +} +item { + name: "drive (e.g., a car, a truck)" + id: 28 +} +item { + name: "eat" + id: 29 +} +item { + name: "enter" + id: 30 +} +item { + name: "hit (an object)" + id: 34 +} +item { + name: "lift/pick up" + id: 36 +} +item { + name: "listen (e.g., to music)" + id: 37 +} +item { + name: "open (e.g., a window, a car door)" + id: 38 +} +item { + name: "play musical instrument" + id: 41 +} +item { + name: "point to (an object)" + id: 43 +} +item { + name: "pull (an object)" + id: 45 +} +item { + name: "push (an object)" + id: 46 +} +item { + name: "put down" + id: 47 +} +item { + name: "read" + id: 48 +} +item { + name: "ride (e.g., a bike, a car, a horse)" + id: 49 +} +item { + name: "sail boat" + id: 51 +} +item { + name: "shoot" + id: 52 +} +item { + name: "smoke" + id: 54 +} +item { + name: "take a photo" + id: 56 +} +item { + name: "text on/look at a cellphone" + id: 57 +} +item { + name: "throw" + id: 58 +} +item { + name: "touch (an object)" + id: 59 +} +item { + name: "turn (e.g., a screwdriver)" + id: 60 +} +item { + name: "watch (e.g., TV)" + id: 61 +} +item { + name: "work on a computer" + id: 62 +} +item { + name: "write" + id: 63 +} +item { + name: "fight/hit (a person)" + id: 64 +} +item { + name: "give/serve (an object) to (a person)" + id: 65 +} +item { + name: "grab (a person)" + id: 66 +} +item { + name: "hand clap" + id: 67 +} +item { + name: "hand shake" + id: 68 +} +item { + name: "hand wave" + id: 69 +} +item { + name: "hug (a person)" + id: 70 +} +item { + name: "kiss (a person)" + id: 72 +} +item { + name: "lift (a person)" + id: 73 +} +item { + name: "listen to (a person)" + id: 74 +} +item { + name: "push (another person)" + id: 76 +} +item { + name: "sing to (e.g., self, a person, a group)" + id: 77 +} +item { + name: "take (an object) from (a person)" + id: 78 +} +item { + name: "talk to (e.g., self, a person, a group)" + id: 79 +} +item { + name: "watch (a person)" + id: 80 +} diff --git a/workspace/virtuallab/object_detection/data/face_label_map.pbtxt b/workspace/virtuallab/object_detection/data/face_label_map.pbtxt new file mode 100644 index 0000000..1c7355d --- /dev/null +++ b/workspace/virtuallab/object_detection/data/face_label_map.pbtxt @@ -0,0 +1,6 @@ +item { + name: "face" + id: 1 + display_name: "face" +} + diff --git a/workspace/virtuallab/object_detection/data/face_person_with_keypoints_label_map.pbtxt b/workspace/virtuallab/object_detection/data/face_person_with_keypoints_label_map.pbtxt new file mode 100644 index 0000000..181f11b --- /dev/null +++ b/workspace/virtuallab/object_detection/data/face_person_with_keypoints_label_map.pbtxt @@ -0,0 +1,102 @@ +item: { + id: 1 + name: 'face' + display_name: 'face' + keypoints { + id: 0 + label: "left_eye_center" + } + keypoints { + id: 1 + label: "right_eye_center" + } + keypoints { + id: 2 + label: "nose_tip" + } + keypoints { + id: 3 + label: "mouth_center" + } + keypoints { + id: 4 + label: "left_ear_tragion" + } + keypoints { + id: 5 + label: "right_ear_tragion" + } +} +item: { + id: 2 + name: 'Person' + display_name: 'PERSON' + keypoints { + id: 6 + label: "NOSE_TIP" + } + keypoints { + id: 7 + label: "LEFT_EYE" + } + keypoints { + id: 8 + label: "RIGHT_EYE" + } + keypoints { + id: 9 + label: "LEFT_EAR_TRAGION" + } + keypoints { + id: 10 + label: "RIGHT_EAR_TRAGION" + } + keypoints { + id: 11 + label: "LEFT_SHOULDER" + } + keypoints { + id: 12 + label: "RIGHT_SHOULDER" + } + keypoints { + id: 13 + label: "LEFT_ELBOW" + } + keypoints { + id: 14 + label: "RIGHT_ELBOW" + } + keypoints { + id: 15 + label: "LEFT_WRIST" + } + keypoints { + id: 16 + label: "RIGHT_WRIST" + } + keypoints { + id: 17 + label: "LEFT_HIP" + } + keypoints { + id: 18 + label: "RIGHT_HIP" + } + keypoints { + id: 19 + label: "LEFT_KNEE" + } + keypoints { + id: 20 + label: "RIGHT_KNEE" + } + keypoints { + id: 21 + label: "LEFT_ANKLE" + } + keypoints { + id: 22 + label: "RIGHT_ANKLE" + } +} diff --git a/workspace/virtuallab/object_detection/data/fgvc_2854_classes_label_map.pbtxt b/workspace/virtuallab/object_detection/data/fgvc_2854_classes_label_map.pbtxt new file mode 100644 index 0000000..009797f --- /dev/null +++ b/workspace/virtuallab/object_detection/data/fgvc_2854_classes_label_map.pbtxt @@ -0,0 +1,14270 @@ +item { + name: "147457" + id: 1 + display_name: "Nicrophorus tomentosus" +} +item { + name: "81923" + id: 2 + display_name: "Halyomorpha halys" +} +item { + name: "7" + id: 3 + display_name: "Aramus guarauna" +} +item { + name: "201041" + id: 4 + display_name: "Rupornis magnirostris" +} +item { + name: "65551" + id: 5 + display_name: "Hyla eximia" +} +item { + name: "106516" + id: 6 + display_name: "Nannothemis bella" +} +item { + name: "154287" + id: 7 + display_name: "Acalymma vittatum" +} +item { + name: "32798" + id: 8 + display_name: "Ramphotyphlops braminus" +} +item { + name: "8229" + id: 9 + display_name: "Cyanocitta cristata" +} +item { + name: "73766" + id: 10 + display_name: "Drymarchon melanurus" +} +item { + name: "409639" + id: 11 + display_name: "Aenetus virescens" +} +item { + name: "8234" + id: 12 + display_name: "Cyanocitta stelleri" +} +item { + name: "228593" + id: 13 + display_name: "Polygrammate hebraeicum" +} +item { + name: "53" + id: 14 + display_name: "Balearica regulorum" +} +item { + name: "57399" + id: 15 + display_name: "Fistularia commersonii" +} +item { + name: "81979" + id: 16 + display_name: "Syritta pipiens" +} +item { + name: "73788" + id: 17 + display_name: "Plestiodon fasciatus" +} +item { + name: "73790" + id: 18 + display_name: "Plestiodon inexpectatus" +} +item { + name: "16447" + id: 19 + display_name: "Pyrocephalus rubinus" +} +item { + name: "73792" + id: 20 + display_name: "Plestiodon laticeps" +} +item { + name: "49219" + id: 21 + display_name: "Anguilla rostrata" +} +item { + name: "73797" + id: 22 + display_name: "Plestiodon obsoletus" +} +item { + name: "73803" + id: 23 + display_name: "Plestiodon tetragrammus" +} +item { + name: "122956" + id: 24 + display_name: "Syntomoides imaon" +} +item { + name: "82003" + id: 25 + display_name: "Arion ater" +} +item { + name: "32854" + id: 26 + display_name: "Chamaeleo dilepis" +} +item { + name: "42341" + id: 27 + display_name: "Tragelaphus scriptus" +} +item { + name: "82018" + id: 28 + display_name: "Taeniopoda eques" +} +item { + name: "57443" + id: 29 + display_name: "Libellula quadrimaculata" +} +item { + name: "4885" + id: 30 + display_name: "Recurvirostra americana" +} +item { + name: "178403" + id: 31 + display_name: "Phalaenophana pyramusalis" +} +item { + name: "135027" + id: 32 + display_name: "Agalychnis dacnicolor" +} +item { + name: "49262" + id: 33 + display_name: "Haemulon sciurus" +} +item { + name: "98417" + id: 34 + display_name: "Cordulegaster diastatops" +} +item { + name: "57458" + id: 35 + display_name: "Ladona julia" +} +item { + name: "115" + id: 36 + display_name: "Ardeotis kori" +} +item { + name: "49269" + id: 37 + display_name: "Diodon holocanthus" +} +item { + name: "57463" + id: 38 + display_name: "Papilio canadensis" +} +item { + name: "82043" + id: 39 + display_name: "Monochamus scutellatus" +} +item { + name: "147580" + id: 40 + display_name: "Ceratotherium simum simum" +} +item { + name: "98430" + id: 41 + display_name: "Cordulia shurtleffii" +} +item { + name: "8319" + id: 42 + display_name: "Pica nuttalli" +} +item { + name: "43712" + id: 43 + display_name: "Dasyprocta punctata" +} +item { + name: "8335" + id: 44 + display_name: "Perisoreus canadensis" +} +item { + name: "508048" + id: 45 + display_name: "Antigone canadensis" +} +item { + name: "49297" + id: 46 + display_name: "Aetobatus narinari" +} +item { + name: "82069" + id: 47 + display_name: "Phyciodes pulchella" +} +item { + name: "73149" + id: 48 + display_name: "Parkesia noveboracensis" +} +item { + name: "180379" + id: 49 + display_name: "Ardea herodias occidentalis" +} +item { + name: "73884" + id: 50 + display_name: "Pantherophis emoryi" +} +item { + name: "106653" + id: 51 + display_name: "Nehalennia irene" +} +item { + name: "73887" + id: 52 + display_name: "Pantherophis guttatus" +} +item { + name: "73888" + id: 53 + display_name: "Pantherophis obsoletus" +} +item { + name: "162" + id: 54 + display_name: "Porzana carolina" +} +item { + name: "245925" + id: 55 + display_name: "Siproeta stelenes biplagiata" +} +item { + name: "117302" + id: 56 + display_name: "Physalia physalis" +} +item { + name: "57516" + id: 57 + display_name: "Bombus terrestris" +} +item { + name: "204995" + id: 58 + display_name: "Anas platyrhynchos diazi" +} +item { + name: "49348" + id: 59 + display_name: "Hyles lineata" +} +item { + name: "82117" + id: 60 + display_name: "Dolomedes tenebrosus" +} +item { + name: "114891" + id: 61 + display_name: "Varanus salvator" +} +item { + name: "319695" + id: 62 + display_name: "Epilachna mexicana" +} +item { + name: "41168" + id: 63 + display_name: "Desmodus rotundus" +} +item { + name: "13688" + id: 64 + display_name: "Motacilla cinerea" +} +item { + name: "57556" + id: 65 + display_name: "Papio ursinus" +} +item { + name: "16598" + id: 66 + display_name: "Empidonax difficilis" +} +item { + name: "16602" + id: 67 + display_name: "Empidonax minimus" +} +item { + name: "16604" + id: 68 + display_name: "Empidonax fulvifrons" +} +item { + name: "409181" + id: 69 + display_name: "Trite planiceps" +} +item { + name: "82144" + id: 70 + display_name: "Hemileuca eglanterina" +} +item { + name: "16611" + id: 71 + display_name: "Empidonax traillii" +} +item { + name: "82153" + id: 72 + display_name: "Ceratomia undulosa" +} +item { + name: "82155" + id: 73 + display_name: "Bittacomorpha clavipes" +} +item { + name: "205036" + id: 74 + display_name: "Xanthorhoe lacustrata" +} +item { + name: "16624" + id: 75 + display_name: "Empidonax hammondii" +} +item { + name: "16625" + id: 76 + display_name: "Empidonax occidentalis" +} +item { + name: "243" + id: 77 + display_name: "Rallus limicola" +} +item { + name: "41" + id: 78 + display_name: "Grus grus" +} +item { + name: "49402" + id: 79 + display_name: "Abudefduf saxatilis" +} +item { + name: "58550" + id: 80 + display_name: "Callophrys niphon" +} +item { + name: "205055" + id: 81 + display_name: "Zopherus nodulosus haldemani" +} +item { + name: "82177" + id: 82 + display_name: "Hermetia illucens" +} +item { + name: "9601" + id: 83 + display_name: "Quiscalus major" +} +item { + name: "7101" + id: 84 + display_name: "Branta leucopsis" +} +item { + name: "8470" + id: 85 + display_name: "Cyanocorax yucatanicus" +} +item { + name: "74009" + id: 86 + display_name: "Zamenis longissimus" +} +item { + name: "8474" + id: 87 + display_name: "Cyanocorax yncas" +} +item { + name: "82204" + id: 88 + display_name: "Nadata gibbosa" +} +item { + name: "123168" + id: 89 + display_name: "Ensatina eschscholtzii xanthoptica" +} +item { + name: "82210" + id: 90 + display_name: "Heterocampa biundata" +} +item { + name: "48284" + id: 91 + display_name: "Oniscus asellus" +} +item { + name: "4146" + id: 92 + display_name: "Oceanites oceanicus" +} +item { + name: "82225" + id: 93 + display_name: "Lophocampa caryae" +} +item { + name: "9609" + id: 94 + display_name: "Quiscalus niger" +} +item { + name: "65849" + id: 95 + display_name: "Incilius nebulifer" +} +item { + name: "207583" + id: 96 + display_name: "Miomantis caffra" +} +item { + name: "491839" + id: 97 + display_name: "Pyrausta insequalis" +} +item { + name: "74048" + id: 98 + display_name: "Alces americanus" +} +item { + name: "57665" + id: 99 + display_name: "Cotinis mutabilis" +} +item { + name: "65860" + id: 100 + display_name: "Incilius valliceps" +} +item { + name: "52911" + id: 101 + display_name: "Dolichovespula maculata" +} +item { + name: "8524" + id: 102 + display_name: "Psilorhinus morio" +} +item { + name: "49491" + id: 103 + display_name: "Thalassoma bifasciatum" +} +item { + name: "41301" + id: 104 + display_name: "Tadarida brasiliensis" +} +item { + name: "57687" + id: 105 + display_name: "Xylocopa varipuncta" +} +item { + name: "57689" + id: 106 + display_name: "Bombus vosnesenskii" +} +item { + name: "57690" + id: 107 + display_name: "Bombus sonorus" +} +item { + name: "33118" + id: 108 + display_name: "Basiliscus vittatus" +} +item { + name: "205151" + id: 109 + display_name: "Phlogophora meticulosa" +} +item { + name: "49504" + id: 110 + display_name: "Callinectes sapidus" +} +item { + name: "16737" + id: 111 + display_name: "Megarynchus pitangua" +} +item { + name: "357" + id: 112 + display_name: "Gallinula tenebrosa" +} +item { + name: "82278" + id: 113 + display_name: "Ameiurus melas" +} +item { + name: "82279" + id: 114 + display_name: "Automeris io" +} +item { + name: "505478" + id: 115 + display_name: "Gallus gallus domesticus" +} +item { + name: "33135" + id: 116 + display_name: "Crotaphytus collaris" +} +item { + name: "41328" + id: 117 + display_name: "Lavia frons" +} +item { + name: "196979" + id: 118 + display_name: "Anaxyrus boreas halophilus" +} +item { + name: "44902" + id: 119 + display_name: "Sigmodon hispidus" +} +item { + name: "1428" + id: 120 + display_name: "Numida meleagris" +} +item { + name: "119153" + id: 121 + display_name: "Junco hyemalis caniceps" +} +item { + name: "49539" + id: 122 + display_name: "Pisaster brevispinus" +} +item { + name: "328068" + id: 123 + display_name: "Belocaulus angustipes" +} +item { + name: "120214" + id: 124 + display_name: "Clostera albosigma" +} +item { + name: "16779" + id: 125 + display_name: "Tyrannus vociferans" +} +item { + name: "16782" + id: 126 + display_name: "Tyrannus tyrannus" +} +item { + name: "16783" + id: 127 + display_name: "Tyrannus forficatus" +} +item { + name: "16784" + id: 128 + display_name: "Tyrannus crassirostris" +} +item { + name: "57745" + id: 129 + display_name: "Linckia laevigata" +} +item { + name: "205202" + id: 130 + display_name: "Ecliptopera silaceata" +} +item { + name: "205203" + id: 131 + display_name: "Dyspteris abortivaria" +} +item { + name: "16791" + id: 132 + display_name: "Tyrannus verticalis" +} +item { + name: "16793" + id: 133 + display_name: "Tyrannus savana" +} +item { + name: "205213" + id: 134 + display_name: "Caripeta divisata" +} +item { + name: "49566" + id: 135 + display_name: "Cicindela sexguttata" +} +item { + name: "491935" + id: 136 + display_name: "Thylacodes squamigerus" +} +item { + name: "205216" + id: 137 + display_name: "Cerma cerintha" +} +item { + name: "39665" + id: 138 + display_name: "Caretta caretta" +} +item { + name: "147881" + id: 139 + display_name: "Trichechus manatus latirostris" +} +item { + name: "28743" + id: 140 + display_name: "Salvadora hexalepis" +} +item { + name: "205231" + id: 141 + display_name: "Idaea dimidiata" +} +item { + name: "205233" + id: 142 + display_name: "Iridopsis larvaria" +} +item { + name: "205235" + id: 143 + display_name: "Leuconycta diphteroides" +} +item { + name: "436" + id: 144 + display_name: "Gallirallus australis" +} +item { + name: "205238" + id: 145 + display_name: "Metanema inatomaria" +} +item { + name: "49591" + id: 146 + display_name: "Lepomis macrochirus" +} +item { + name: "229817" + id: 147 + display_name: "Raphia frater" +} +item { + name: "49594" + id: 148 + display_name: "Pomoxis nigromaculatus" +} +item { + name: "65979" + id: 149 + display_name: "Lithobates catesbeianus" +} +item { + name: "49596" + id: 150 + display_name: "Salvelinus fontinalis" +} +item { + name: "65982" + id: 151 + display_name: "Lithobates clamitans" +} +item { + name: "8649" + id: 152 + display_name: "Calocitta formosa" +} +item { + name: "8650" + id: 153 + display_name: "Calocitta colliei" +} +item { + name: "82379" + id: 154 + display_name: "Hemaris thysbe" +} +item { + name: "49614" + id: 155 + display_name: "Lepomis gibbosus" +} +item { + name: "63028" + id: 156 + display_name: "Hypercompe scribonia" +} +item { + name: "39672" + id: 157 + display_name: "Eretmochelys imbricata" +} +item { + name: "66003" + id: 158 + display_name: "Lithobates pipiens" +} +item { + name: "197077" + id: 159 + display_name: "Vanessa kershawi" +} +item { + name: "473" + id: 160 + display_name: "Fulica americana" +} +item { + name: "147930" + id: 161 + display_name: "Rabidosa rabida" +} +item { + name: "147931" + id: 162 + display_name: "Panoquina ocola" +} +item { + name: "66012" + id: 163 + display_name: "Lithobates sylvaticus" +} +item { + name: "8671" + id: 164 + display_name: "Pachyramphus aglaiae" +} +item { + name: "41440" + id: 165 + display_name: "Phocoena phocoena" +} +item { + name: "27388" + id: 166 + display_name: "Carphophis amoenus" +} +item { + name: "82418" + id: 167 + display_name: "Cicindela punctulata" +} +item { + name: "25078" + id: 168 + display_name: "Gastrophryne carolinensis" +} +item { + name: "82425" + id: 169 + display_name: "Cicindela repanda" +} +item { + name: "143446" + id: 170 + display_name: "Paonias myops" +} +item { + name: "41478" + id: 171 + display_name: "Eschrichtius robustus" +} +item { + name: "5200" + id: 172 + display_name: "Buteo lagopus" +} +item { + name: "148908" + id: 173 + display_name: "Chrysodeixis includens" +} +item { + name: "41482" + id: 174 + display_name: "Tursiops truncatus" +} +item { + name: "6914" + id: 175 + display_name: "Cygnus atratus" +} +item { + name: "464301" + id: 176 + display_name: "Philesturnus rufusater" +} +item { + name: "129226" + id: 177 + display_name: "Chytolita morbidalis" +} +item { + name: "180759" + id: 178 + display_name: "Aphonopelma iodius" +} +item { + name: "135318" + id: 179 + display_name: "Apantesis phalerata" +} +item { + name: "49699" + id: 180 + display_name: "Pisaster ochraceus" +} +item { + name: "49700" + id: 181 + display_name: "Coluber lateralis lateralis" +} +item { + name: "61532" + id: 182 + display_name: "Propylea quatuordecimpunctata" +} +item { + name: "4368" + id: 183 + display_name: "Larus marinus" +} +item { + name: "41521" + id: 184 + display_name: "Orcinus orca" +} +item { + name: "49716" + id: 185 + display_name: "Paonias excaecata" +} +item { + name: "41526" + id: 186 + display_name: "Delphinus delphis" +} +item { + name: "49723" + id: 187 + display_name: "Pugettia producta" +} +item { + name: "16956" + id: 188 + display_name: "Pitangus sulphuratus" +} +item { + name: "210607" + id: 189 + display_name: "Diastictis fracturalis" +} +item { + name: "148030" + id: 190 + display_name: "Equus asinus" +} +item { + name: "6924" + id: 191 + display_name: "Anas rubripes" +} +item { + name: "30844" + id: 192 + display_name: "Bothriechis schlegelii" +} +item { + name: "123628" + id: 193 + display_name: "Argynnis paphia" +} +item { + name: "131676" + id: 194 + display_name: "Anthus novaeseelandiae novaeseelandiae" +} +item { + name: "41566" + id: 195 + display_name: "Megaptera novaeangliae" +} +item { + name: "49759" + id: 196 + display_name: "Pyrgus oileus" +} +item { + name: "49761" + id: 197 + display_name: "Anartia jatrophae" +} +item { + name: "49766" + id: 198 + display_name: "Heliconius charithonia" +} +item { + name: "33383" + id: 199 + display_name: "Coleonyx brevis" +} +item { + name: "33384" + id: 200 + display_name: "Coleonyx elegans" +} +item { + name: "312764" + id: 201 + display_name: "Euptoieta hegesia meridiania" +} +item { + name: "82538" + id: 202 + display_name: "Vanessa gonerilla" +} +item { + name: "33387" + id: 203 + display_name: "Coleonyx variegatus" +} +item { + name: "56082" + id: 204 + display_name: "Aeshna canadensis" +} +item { + name: "17008" + id: 205 + display_name: "Sayornis phoebe" +} +item { + name: "200808" + id: 206 + display_name: "Sceloporus graciosus vandenburgianus" +} +item { + name: "17013" + id: 207 + display_name: "Sayornis nigricans" +} +item { + name: "122381" + id: 208 + display_name: "Cupido comyntas" +} +item { + name: "123516" + id: 209 + display_name: "Mydas clavatus" +} +item { + name: "8834" + id: 210 + display_name: "Tityra semifasciata" +} +item { + name: "146199" + id: 211 + display_name: "Lampropeltis californiae" +} +item { + name: "17858" + id: 212 + display_name: "Dryocopus lineatus" +} +item { + name: "334616" + id: 213 + display_name: "Battus philenor hirsuta" +} +item { + name: "82582" + id: 214 + display_name: "Labidomera clivicollis" +} +item { + name: "204699" + id: 215 + display_name: "Pseudothyatira cymatophoroides" +} +item { + name: "41638" + id: 216 + display_name: "Ursus americanus" +} +item { + name: "27420" + id: 217 + display_name: "Desmognathus fuscus" +} +item { + name: "81584" + id: 218 + display_name: "Anisota virginiensis" +} +item { + name: "49848" + id: 219 + display_name: "Navanax inermis" +} +item { + name: "143476" + id: 220 + display_name: "Calledapteryx dryopterata" +} +item { + name: "41663" + id: 221 + display_name: "Procyon lotor" +} +item { + name: "49857" + id: 222 + display_name: "Aplysia vaccaria" +} +item { + name: "41673" + id: 223 + display_name: "Nasua narica" +} +item { + name: "41676" + id: 224 + display_name: "Bassariscus astutus" +} +item { + name: "27427" + id: 225 + display_name: "Aneides lugubris" +} +item { + name: "418530" + id: 226 + display_name: "Porphyrio melanotus" +} +item { + name: "311419" + id: 227 + display_name: "Neobernaya spadicea" +} +item { + name: "113502" + id: 228 + display_name: "Sympetrum costiferum" +} +item { + name: "66278" + id: 229 + display_name: "Oophaga pumilio" +} +item { + name: "6951" + id: 230 + display_name: "Anas bahamensis" +} +item { + name: "213740" + id: 231 + display_name: "Antaeotricha schlaegeri" +} +item { + name: "143485" + id: 232 + display_name: "Xanthorhoe ferrugata" +} +item { + name: "120275" + id: 233 + display_name: "Euphyia intermediata" +} +item { + name: "48035" + id: 234 + display_name: "Strongylocentrotus purpuratus" +} +item { + name: "41728" + id: 235 + display_name: "Mirounga angustirostris" +} +item { + name: "41733" + id: 236 + display_name: "Halichoerus grypus" +} +item { + name: "41740" + id: 237 + display_name: "Zalophus californianus" +} +item { + name: "118914" + id: 238 + display_name: "Echinargus isola" +} +item { + name: "4936" + id: 239 + display_name: "Egretta novaehollandiae" +} +item { + name: "131862" + id: 240 + display_name: "Typocerus velutinus" +} +item { + name: "55401" + id: 241 + display_name: "Pieris brassicae" +} +item { + name: "41752" + id: 242 + display_name: "Arctocephalus forsteri" +} +item { + name: "41755" + id: 243 + display_name: "Eumetopias jubatus" +} +item { + name: "123676" + id: 244 + display_name: "Anas crecca carolinensis" +} +item { + name: "41763" + id: 245 + display_name: "Phocarctos hookeri" +} +item { + name: "181034" + id: 246 + display_name: "Cervus elaphus canadensis" +} +item { + name: "49964" + id: 247 + display_name: "Ginglymostoma cirratum" +} +item { + name: "213809" + id: 248 + display_name: "Anticarsia gemmatalis" +} +item { + name: "49972" + id: 249 + display_name: "Battus philenor" +} +item { + name: "205623" + id: 250 + display_name: "Microstylum morosum" +} +item { + name: "336697" + id: 251 + display_name: "Arctia villica" +} +item { + name: "41789" + id: 252 + display_name: "Taxidea taxus" +} +item { + name: "48724" + id: 253 + display_name: "Phidiana hiltoni" +} +item { + name: "123713" + id: 254 + display_name: "Neoscona oaxacensis" +} +item { + name: "33602" + id: 255 + display_name: "Tarentola mauritanica" +} +item { + name: "846" + id: 256 + display_name: "Alectoris chukar" +} +item { + name: "41808" + id: 257 + display_name: "Mustela erminea" +} +item { + name: "50001" + id: 258 + display_name: "Terrapene carolina carolina" +} +item { + name: "41810" + id: 259 + display_name: "Mustela frenata" +} +item { + name: "82774" + id: 260 + display_name: "Oryctes nasicornis" +} +item { + name: "41815" + id: 261 + display_name: "Mustela nivalis" +} +item { + name: "4239" + id: 262 + display_name: "Tachybaptus dominicus" +} +item { + name: "344926" + id: 263 + display_name: "Artemisiospiza belli" +} +item { + name: "82792" + id: 264 + display_name: "Celastrina neglecta" +} +item { + name: "41841" + id: 265 + display_name: "Meles meles" +} +item { + name: "882" + id: 266 + display_name: "Gallus gallus" +} +item { + name: "125758" + id: 267 + display_name: "Mercenaria mercenaria" +} +item { + name: "9081" + id: 268 + display_name: "Cardinalis sinuatus" +} +item { + name: "9083" + id: 269 + display_name: "Cardinalis cardinalis" +} +item { + name: "9092" + id: 270 + display_name: "Melospiza lincolnii" +} +item { + name: "4246" + id: 271 + display_name: "Podilymbus podiceps" +} +item { + name: "9096" + id: 272 + display_name: "Melospiza georgiana" +} +item { + name: "906" + id: 273 + display_name: "Meleagris gallopavo" +} +item { + name: "50059" + id: 274 + display_name: "Limacia cockerelli" +} +item { + name: "394124" + id: 275 + display_name: "Orthodera novaezealandiae" +} +item { + name: "82832" + id: 276 + display_name: "Cosmopepla lintneriana" +} +item { + name: "913" + id: 277 + display_name: "Meleagris ocellata" +} +item { + name: "41877" + id: 278 + display_name: "Conepatus leuconotus" +} +item { + name: "196419" + id: 279 + display_name: "Euborellia annulipes" +} +item { + name: "50071" + id: 280 + display_name: "Erynnis horatius" +} +item { + name: "41880" + id: 281 + display_name: "Mephitis mephitis" +} +item { + name: "50073" + id: 282 + display_name: "Dryas iulia" +} +item { + name: "173793" + id: 283 + display_name: "Diphthera festiva" +} +item { + name: "41886" + id: 284 + display_name: "Crocuta crocuta" +} +item { + name: "30683" + id: 285 + display_name: "Agkistrodon contortrix contortrix" +} +item { + name: "931" + id: 286 + display_name: "Lagopus lagopus" +} +item { + name: "41901" + id: 287 + display_name: "Herpestes javanicus" +} +item { + name: "143517" + id: 288 + display_name: "Biston betularia" +} +item { + name: "9139" + id: 289 + display_name: "Spizella atrogularis" +} +item { + name: "8350" + id: 290 + display_name: "Pyrrhocorax graculus" +} +item { + name: "9144" + id: 291 + display_name: "Spizella breweri" +} +item { + name: "12936" + id: 292 + display_name: "Sialia currucoides" +} +item { + name: "9152" + id: 293 + display_name: "Spizella pusilla" +} +item { + name: "68229" + id: 294 + display_name: "Tramea carolina" +} +item { + name: "6987" + id: 295 + display_name: "Anas superciliosa" +} +item { + name: "9156" + id: 296 + display_name: "Passerella iliaca" +} +item { + name: "202315" + id: 297 + display_name: "Romaleon antennarium" +} +item { + name: "4257" + id: 298 + display_name: "Phoenicopterus ruber" +} +item { + name: "25545" + id: 299 + display_name: "Rana aurora" +} +item { + name: "15282" + id: 300 + display_name: "Sylvia atricapilla" +} +item { + name: "103927" + id: 301 + display_name: "Ladona deplanata" +} +item { + name: "17356" + id: 302 + display_name: "Vireo bellii" +} +item { + name: "26765" + id: 303 + display_name: "Ambystoma mavortium" +} +item { + name: "205777" + id: 304 + display_name: "Plectrodera scalator" +} +item { + name: "17362" + id: 305 + display_name: "Vireo plumbeus" +} +item { + name: "99283" + id: 306 + display_name: "Didymops transversa" +} +item { + name: "17364" + id: 307 + display_name: "Vireo philadelphicus" +} +item { + name: "17365" + id: 308 + display_name: "Vireo flavifrons" +} +item { + name: "17366" + id: 309 + display_name: "Vireo olivaceus" +} +item { + name: "9182" + id: 310 + display_name: "Zonotrichia querula" +} +item { + name: "17375" + id: 311 + display_name: "Vireo huttoni" +} +item { + name: "9184" + id: 312 + display_name: "Zonotrichia albicollis" +} +item { + name: "9185" + id: 313 + display_name: "Zonotrichia atricapilla" +} +item { + name: "50147" + id: 314 + display_name: "Celithemis eponina" +} +item { + name: "47585" + id: 315 + display_name: "Crassostrea virginica" +} +item { + name: "9195" + id: 316 + display_name: "Emberiza citrinella" +} +item { + name: "41964" + id: 317 + display_name: "Panthera leo" +} +item { + name: "6994" + id: 318 + display_name: "Bucephala islandica" +} +item { + name: "52506" + id: 319 + display_name: "Adalia bipunctata" +} +item { + name: "9201" + id: 320 + display_name: "Emberiza schoeniclus" +} +item { + name: "17394" + id: 321 + display_name: "Vireo gilvus" +} +item { + name: "25591" + id: 322 + display_name: "Rana temporaria" +} +item { + name: "41976" + id: 323 + display_name: "Lynx rufus" +} +item { + name: "214015" + id: 324 + display_name: "Apoda y-inversum" +} +item { + name: "50176" + id: 325 + display_name: "Enallagma vesperum" +} +item { + name: "99331" + id: 326 + display_name: "Diplacodes trivialis" +} +item { + name: "50181" + id: 327 + display_name: "Loxosceles reclusa" +} +item { + name: "74758" + id: 328 + display_name: "Neovison vison" +} +item { + name: "123912" + id: 329 + display_name: "Charaxes jasius" +} +item { + name: "41997" + id: 330 + display_name: "Leopardus pardalis" +} +item { + name: "123920" + id: 331 + display_name: "Dorcus parallelipipedus" +} +item { + name: "132334" + id: 332 + display_name: "Urbanus procne" +} +item { + name: "123922" + id: 333 + display_name: "Abudefduf sordidus" +} +item { + name: "9236" + id: 334 + display_name: "Serinus serinus" +} +item { + name: "42007" + id: 335 + display_name: "Puma concolor" +} +item { + name: "9240" + id: 336 + display_name: "Serinus mozambicus" +} +item { + name: "148506" + id: 337 + display_name: "Melanis pixe" +} +item { + name: "58399" + id: 338 + display_name: "Urosalpinx cinerea" +} +item { + name: "312353" + id: 339 + display_name: "Leptophobia aripa elodia" +} +item { + name: "148517" + id: 340 + display_name: "Heliopetes laviana" +} +item { + name: "73905" + id: 341 + display_name: "Phrynosoma cornutum" +} +item { + name: "39772" + id: 342 + display_name: "Chrysemys picta marginata" +} +item { + name: "25646" + id: 343 + display_name: "Rana boylii" +} +item { + name: "62984" + id: 344 + display_name: "Aedes albopictus" +} +item { + name: "123959" + id: 345 + display_name: "Ensatina eschscholtzii oregonensis" +} +item { + name: "1081" + id: 346 + display_name: "Lophura leucomelanos" +} +item { + name: "39775" + id: 347 + display_name: "Chrysemys picta picta" +} +item { + name: "42046" + id: 348 + display_name: "Canis mesomelas" +} +item { + name: "42048" + id: 349 + display_name: "Canis lupus" +} +item { + name: "42051" + id: 350 + display_name: "Canis latrans" +} +item { + name: "9284" + id: 351 + display_name: "Euphonia elegantissima" +} +item { + name: "25669" + id: 352 + display_name: "Rana dalmatina" +} +item { + name: "9287" + id: 353 + display_name: "Euphonia hirundinacea" +} +item { + name: "9291" + id: 354 + display_name: "Euphonia affinis" +} +item { + name: "222284" + id: 355 + display_name: "Iridopsis defectaria" +} +item { + name: "74832" + id: 356 + display_name: "Papio anubis" +} +item { + name: "148563" + id: 357 + display_name: "Myscelia ethusa" +} +item { + name: "42069" + id: 358 + display_name: "Vulpes vulpes" +} +item { + name: "9743" + id: 359 + display_name: "Agelaius tricolor" +} +item { + name: "42076" + id: 360 + display_name: "Urocyon cinereoargenteus" +} +item { + name: "509025" + id: 361 + display_name: "Momotus lessonii" +} +item { + name: "17506" + id: 362 + display_name: "Zosterops japonicus" +} +item { + name: "4283" + id: 363 + display_name: "Phalacrocorax pelagicus" +} +item { + name: "58469" + id: 364 + display_name: "Thorybes pylades" +} +item { + name: "9319" + id: 365 + display_name: "Icterus cucullatus" +} +item { + name: "58473" + id: 366 + display_name: "Erynnis icelus" +} +item { + name: "58475" + id: 367 + display_name: "Erynnis juvenalis" +} +item { + name: "42093" + id: 368 + display_name: "Lycaon pictus" +} +item { + name: "58478" + id: 369 + display_name: "Erynnis baptisiae" +} +item { + name: "9328" + id: 370 + display_name: "Icterus graduacauda" +} +item { + name: "58481" + id: 371 + display_name: "Ancyloxypha numitor" +} +item { + name: "132210" + id: 372 + display_name: "Deloyala guttata" +} +item { + name: "58484" + id: 373 + display_name: "Thymelicus lineola" +} +item { + name: "13701" + id: 374 + display_name: "Motacilla aguimp" +} +item { + name: "410743" + id: 375 + display_name: "Anas superciliosa \303\227 platyrhynchos" +} +item { + name: "9336" + id: 376 + display_name: "Icterus pustulatus" +} +item { + name: "9339" + id: 377 + display_name: "Icterus gularis" +} +item { + name: "124031" + id: 378 + display_name: "Agrius convolvuli" +} +item { + name: "42113" + id: 379 + display_name: "Pecari tajacu" +} +item { + name: "132227" + id: 380 + display_name: "Lethe appalachia" +} +item { + name: "113516" + id: 381 + display_name: "Sympetrum madidum" +} +item { + name: "58509" + id: 382 + display_name: "Anatrytone logan" +} +item { + name: "83086" + id: 383 + display_name: "Eurytides marcellus" +} +item { + name: "58511" + id: 384 + display_name: "Poanes viator" +} +item { + name: "83090" + id: 385 + display_name: "Epimecis hortaria" +} +item { + name: "115859" + id: 386 + display_name: "Micrurus tener tener" +} +item { + name: "129902" + id: 387 + display_name: "Camponotus pennsylvanicus" +} +item { + name: "42134" + id: 388 + display_name: "Sus scrofa" +} +item { + name: "58519" + id: 389 + display_name: "Pompeius verna" +} +item { + name: "205977" + id: 390 + display_name: "Coccinella undecimpunctata" +} +item { + name: "58523" + id: 391 + display_name: "Papilio polyxenes" +} +item { + name: "58525" + id: 392 + display_name: "Papilio troilus" +} +item { + name: "410783" + id: 393 + display_name: "Hypoblemum albovittatum" +} +item { + name: "9376" + id: 394 + display_name: "Carduelis cannabina" +} +item { + name: "58531" + id: 395 + display_name: "Colias philodice" +} +item { + name: "50340" + id: 396 + display_name: "Hylephila phyleus" +} +item { + name: "42149" + id: 397 + display_name: "Hippopotamus amphibius" +} +item { + name: "50342" + id: 398 + display_name: "Erythrodiplax umbrata" +} +item { + name: "12883" + id: 399 + display_name: "Catharus minimus" +} +item { + name: "28557" + id: 400 + display_name: "Storeria occipitomaculata" +} +item { + name: "199" + id: 401 + display_name: "Amaurornis phoenicurus" +} +item { + name: "58541" + id: 402 + display_name: "Satyrium liparops" +} +item { + name: "58543" + id: 403 + display_name: "Callophrys augustinus" +} +item { + name: "42161" + id: 404 + display_name: "Dama dama" +} +item { + name: "61508" + id: 405 + display_name: "Ischnura elegans" +} +item { + name: "1204" + id: 406 + display_name: "Pavo cristatus" +} +item { + name: "42166" + id: 407 + display_name: "Axis axis" +} +item { + name: "146797" + id: 408 + display_name: "Platynota idaeusalis" +} +item { + name: "58556" + id: 409 + display_name: "Celastrina ladon" +} +item { + name: "367477" + id: 410 + display_name: "Rallus crepitans" +} +item { + name: "58561" + id: 411 + display_name: "Libytheana carinenta" +} +item { + name: "58563" + id: 412 + display_name: "Speyeria aphrodite" +} +item { + name: "58564" + id: 413 + display_name: "Boloria bellona" +} +item { + name: "413489" + id: 414 + display_name: "Nestor meridionalis septentrionalis" +} +item { + name: "42184" + id: 415 + display_name: "Capreolus capreolus" +} +item { + name: "9419" + id: 416 + display_name: "Pipilo chlorurus" +} +item { + name: "9420" + id: 417 + display_name: "Pipilo maculatus" +} +item { + name: "9424" + id: 418 + display_name: "Pipilo erythrophthalmus" +} +item { + name: "99539" + id: 419 + display_name: "Dorocordulia libera" +} +item { + name: "58580" + id: 420 + display_name: "Polygonia progne" +} +item { + name: "58581" + id: 421 + display_name: "Nymphalis vaualbum" +} +item { + name: "42199" + id: 422 + display_name: "Rangifer tarandus" +} +item { + name: "58586" + id: 423 + display_name: "Limenitis archippus" +} +item { + name: "58587" + id: 424 + display_name: "Asterocampa clyton" +} +item { + name: "42206" + id: 425 + display_name: "Cervus elaphus" +} +item { + name: "312543" + id: 426 + display_name: "Anartia jatrophae luteipicta" +} +item { + name: "204094" + id: 427 + display_name: "Cairina moschata domestica" +} +item { + name: "4304" + id: 428 + display_name: "Phalacrocorax varius" +} +item { + name: "42210" + id: 429 + display_name: "Cervus nippon" +} +item { + name: "17638" + id: 430 + display_name: "Picoides dorsalis" +} +item { + name: "132330" + id: 431 + display_name: "Chlosyne janais" +} +item { + name: "58603" + id: 432 + display_name: "Megisto cymela" +} +item { + name: "42220" + id: 433 + display_name: "Odocoileus hemionus" +} +item { + name: "17645" + id: 434 + display_name: "Picoides nuttallii" +} +item { + name: "58606" + id: 435 + display_name: "Cercyonis pegala" +} +item { + name: "42223" + id: 436 + display_name: "Odocoileus virginianus" +} +item { + name: "58609" + id: 437 + display_name: "Lepisosteus osseus" +} +item { + name: "17650" + id: 438 + display_name: "Picoides scalaris" +} +item { + name: "132339" + id: 439 + display_name: "Anthanassa texana" +} +item { + name: "58612" + id: 440 + display_name: "Carassius auratus" +} +item { + name: "1406" + id: 441 + display_name: "Callipepla gambelii" +} +item { + name: "9462" + id: 442 + display_name: "Pyrrhula pyrrhula" +} +item { + name: "4308" + id: 443 + display_name: "Phalacrocorax brasilianus" +} +item { + name: "17660" + id: 444 + display_name: "Picoides pubescens" +} +item { + name: "1280" + id: 445 + display_name: "Colinus virginianus" +} +item { + name: "129920" + id: 446 + display_name: "Calliostoma ligatum" +} +item { + name: "58627" + id: 447 + display_name: "Perca flavescens" +} +item { + name: "148742" + id: 448 + display_name: "Hamadryas februa" +} +item { + name: "39809" + id: 449 + display_name: "Terrapene ornata ornata" +} +item { + name: "115979" + id: 450 + display_name: "Plestiodon skiltonianus skiltonianus" +} +item { + name: "9484" + id: 451 + display_name: "Sporophila torqueola" +} +item { + name: "17678" + id: 452 + display_name: "Picoides villosus" +} +item { + name: "3862" + id: 453 + display_name: "Calidris pusilla" +} +item { + name: "70421" + id: 454 + display_name: "Acris blanchardi" +} +item { + name: "124183" + id: 455 + display_name: "Phlogophora periculosa" +} +item { + name: "124184" + id: 456 + display_name: "Plodia interpunctella" +} +item { + name: "99609" + id: 457 + display_name: "Dromogomphus spinosus" +} +item { + name: "99610" + id: 458 + display_name: "Dromogomphus spoliatus" +} +item { + name: "17694" + id: 459 + display_name: "Picoides arcticus" +} +item { + name: "113521" + id: 460 + display_name: "Sympetrum pallipes" +} +item { + name: "320801" + id: 461 + display_name: "Aspidoscelis tesselata" +} +item { + name: "7047" + id: 462 + display_name: "Aythya marila" +} +item { + name: "4317" + id: 463 + display_name: "Phaethon aethereus" +} +item { + name: "81606" + id: 464 + display_name: "Littorina littorea" +} +item { + name: "99891" + id: 465 + display_name: "Enallagma aspersum" +} +item { + name: "9528" + id: 466 + display_name: "Sturnella magna" +} +item { + name: "99641" + id: 467 + display_name: "Dythemis fugax" +} +item { + name: "99644" + id: 468 + display_name: "Dythemis nigrescens" +} +item { + name: "39818" + id: 469 + display_name: "Terrapene carolina triunguis" +} +item { + name: "99647" + id: 470 + display_name: "Dythemis velox" +} +item { + name: "148800" + id: 471 + display_name: "Chioides albofasciatus" +} +item { + name: "19339" + id: 472 + display_name: "Melopsittacus undulatus" +} +item { + name: "47509" + id: 473 + display_name: "Diaulula sandiegensis" +} +item { + name: "148810" + id: 474 + display_name: "Anaea aidea" +} +item { + name: "123070" + id: 475 + display_name: "Capra hircus" +} +item { + name: "7054" + id: 476 + display_name: "Aythya affinis" +} +item { + name: "99897" + id: 477 + display_name: "Enallagma civile" +} +item { + name: "42328" + id: 478 + display_name: "Kobus ellipsiprymnus" +} +item { + name: "48328" + id: 479 + display_name: "Aurelia aurita" +} +item { + name: "132445" + id: 480 + display_name: "Conchylodes ovulalis" +} +item { + name: "215271" + id: 481 + display_name: "Bleptina caradrinalis" +} +item { + name: "83297" + id: 482 + display_name: "Scarus rubroviolaceus" +} +item { + name: "42347" + id: 483 + display_name: "Rupicapra rupicapra" +} +item { + name: "7058" + id: 484 + display_name: "Aythya novaeseelandiae" +} +item { + name: "52457" + id: 485 + display_name: "Chaetodon auriga" +} +item { + name: "1392" + id: 486 + display_name: "Cyrtonyx montezumae" +} +item { + name: "4328" + id: 487 + display_name: "Pelecanus occidentalis" +} +item { + name: "7647" + id: 488 + display_name: "Cinclus cinclus" +} +item { + name: "148856" + id: 489 + display_name: "Anteos clorinde" +} +item { + name: "7060" + id: 490 + display_name: "Chen rossii" +} +item { + name: "58750" + id: 491 + display_name: "Nomophila nearctica" +} +item { + name: "1409" + id: 492 + display_name: "Callipepla californica" +} +item { + name: "9602" + id: 493 + display_name: "Quiscalus quiscula" +} +item { + name: "296326" + id: 494 + display_name: "Oncopeltus sexmaculatus" +} +item { + name: "9607" + id: 495 + display_name: "Quiscalus mexicanus" +} +item { + name: "319724" + id: 496 + display_name: "Euphoria kernii" +} +item { + name: "1419" + id: 497 + display_name: "Callipepla squamata" +} +item { + name: "148883" + id: 498 + display_name: "Eantis tamenund" +} +item { + name: "42391" + id: 499 + display_name: "Ovis canadensis" +} +item { + name: "107937" + id: 500 + display_name: "Orthemis discolor" +} +item { + name: "42405" + id: 501 + display_name: "Syncerus caffer" +} +item { + name: "42408" + id: 502 + display_name: "Bison bison" +} +item { + name: "116137" + id: 503 + display_name: "Sceloporus cowlesi" +} +item { + name: "326296" + id: 504 + display_name: "Bufo bufo" +} +item { + name: "148907" + id: 505 + display_name: "Cydia latiferreana" +} +item { + name: "42414" + id: 506 + display_name: "Oreamnos americanus" +} +item { + name: "116143" + id: 507 + display_name: "Sceloporus tristichus" +} +item { + name: "99912" + id: 508 + display_name: "Enallagma geminatum" +} +item { + name: "226889" + id: 509 + display_name: "Pangrapta decoralis" +} +item { + name: "42429" + id: 510 + display_name: "Antilocapra americana" +} +item { + name: "17855" + id: 511 + display_name: "Dryocopus pileatus" +} +item { + name: "107974" + id: 512 + display_name: "Orthetrum sabina" +} +item { + name: "56225" + id: 513 + display_name: "Polygonia c-album" +} +item { + name: "67016" + id: 514 + display_name: "Rana draytonii" +} +item { + name: "132553" + id: 515 + display_name: "Strymon istapa" +} +item { + name: "73155" + id: 516 + display_name: "Passerina caerulea" +} +item { + name: "26074" + id: 517 + display_name: "Crocodylus moreletii" +} +item { + name: "171903" + id: 518 + display_name: "Oligyra orbiculata" +} +item { + name: "26085" + id: 519 + display_name: "Crocodylus acutus" +} +item { + name: "143613" + id: 520 + display_name: "Homophoberia apicosa" +} +item { + name: "5715" + id: 521 + display_name: "Amazilia beryllina" +} +item { + name: "9721" + id: 522 + display_name: "Geothlypis trichas" +} +item { + name: "154446" + id: 523 + display_name: "Lambdina fiscellaria" +} +item { + name: "236841" + id: 524 + display_name: "Lichanura orcutti" +} +item { + name: "20737" + id: 525 + display_name: "Trogon melanocephalus" +} +item { + name: "124431" + id: 526 + display_name: "Cycloneda sanguinea" +} +item { + name: "124432" + id: 527 + display_name: "Deroceras reticulatum" +} +item { + name: "39566" + id: 528 + display_name: "Apalone ferox" +} +item { + name: "149017" + id: 529 + display_name: "Chlorochlamys chloroleucaria" +} +item { + name: "15281" + id: 530 + display_name: "Sylvia communis" +} +item { + name: "312873" + id: 531 + display_name: "Anartia fatima fatima" +} +item { + name: "9771" + id: 532 + display_name: "Pinicola enucleator" +} +item { + name: "39858" + id: 533 + display_name: "Graptemys geographica" +} +item { + name: "26159" + id: 534 + display_name: "Alligator mississippiensis" +} +item { + name: "304690" + id: 535 + display_name: "Naupactus cervinus" +} +item { + name: "124467" + id: 536 + display_name: "Pseudosphinx tetrio" +} +item { + name: "99892" + id: 537 + display_name: "Enallagma basidens" +} +item { + name: "99895" + id: 538 + display_name: "Enallagma carunculatum" +} +item { + name: "67129" + id: 539 + display_name: "Rhinella marina" +} +item { + name: "83515" + id: 540 + display_name: "Oxybelis aeneus" +} +item { + name: "81681" + id: 541 + display_name: "Campaea perlata" +} +item { + name: "99901" + id: 542 + display_name: "Enallagma cyathigerum" +} +item { + name: "99911" + id: 543 + display_name: "Enallagma exsulans" +} +item { + name: "9800" + id: 544 + display_name: "Coccothraustes vespertinus" +} +item { + name: "9801" + id: 545 + display_name: "Coccothraustes coccothraustes" +} +item { + name: "154551" + id: 546 + display_name: "Leptoglossus zonatus" +} +item { + name: "9807" + id: 547 + display_name: "Vermivora chrysoptera" +} +item { + name: "61157" + id: 548 + display_name: "Trichodes ornatus" +} +item { + name: "99924" + id: 549 + display_name: "Enallagma signatum" +} +item { + name: "1626" + id: 550 + display_name: "Opisthocomus hoazin" +} +item { + name: "132704" + id: 551 + display_name: "Setophaga coronata coronata" +} +item { + name: "119056" + id: 552 + display_name: "Centruroides vittatus" +} +item { + name: "50786" + id: 553 + display_name: "Vanessa annabella" +} +item { + name: "60347" + id: 554 + display_name: "Pituophis catenifer sayi" +} +item { + name: "9833" + id: 555 + display_name: "Diglossa baritula" +} +item { + name: "132718" + id: 556 + display_name: "Scathophaga stercoraria" +} +item { + name: "132719" + id: 557 + display_name: "Calopteron reticulatum" +} +item { + name: "116340" + id: 558 + display_name: "Dreissena polymorpha" +} +item { + name: "134078" + id: 559 + display_name: "Scoliopteryx libatrix" +} +item { + name: "9850" + id: 560 + display_name: "Saltator coerulescens" +} +item { + name: "117695" + id: 561 + display_name: "Cucumaria miniata" +} +item { + name: "9854" + id: 562 + display_name: "Saltator atriceps" +} +item { + name: "132736" + id: 563 + display_name: "Urola nivalis" +} +item { + name: "34435" + id: 564 + display_name: "Hemidactylus turcicus" +} +item { + name: "9864" + id: 565 + display_name: "Sicalis flaveola" +} +item { + name: "7106" + id: 566 + display_name: "Aix galericulata" +} +item { + name: "485010" + id: 567 + display_name: "Chinavia hilaris" +} +item { + name: "132764" + id: 568 + display_name: "Junco hyemalis hyemalis" +} +item { + name: "367558" + id: 569 + display_name: "Eupsittula canicularis" +} +item { + name: "370351" + id: 570 + display_name: "Microcarbo melanoleucos" +} +item { + name: "50867" + id: 571 + display_name: "Argiope bruennichi" +} +item { + name: "67252" + id: 572 + display_name: "Trachycephalus typhonius" +} +item { + name: "132789" + id: 573 + display_name: "Clepsis peritana" +} +item { + name: "9915" + id: 574 + display_name: "Piranga rubra" +} +item { + name: "50880" + id: 575 + display_name: "Limenitis lorquini" +} +item { + name: "9921" + id: 576 + display_name: "Piranga olivacea" +} +item { + name: "100034" + id: 577 + display_name: "Epiaeschna heros" +} +item { + name: "9924" + id: 578 + display_name: "Piranga flava" +} +item { + name: "42339" + id: 579 + display_name: "Tragelaphus strepsiceros" +} +item { + name: "50892" + id: 580 + display_name: "Euphydryas chalcedona" +} +item { + name: "130348" + id: 581 + display_name: "Dione moneta" +} +item { + name: "394966" + id: 582 + display_name: "Phaulacridium marginale" +} +item { + name: "9943" + id: 583 + display_name: "Amphispiza bilineata" +} +item { + name: "4388" + id: 584 + display_name: "Larus dominicanus" +} +item { + name: "1758" + id: 585 + display_name: "Piaya cayana" +} +item { + name: "50913" + id: 586 + display_name: "Hyalophora euryalus" +} +item { + name: "9958" + id: 587 + display_name: "Aimophila ruficeps" +} +item { + name: "59115" + id: 588 + display_name: "Gambusia affinis" +} +item { + name: "64346" + id: 589 + display_name: "Natrix tessellata" +} +item { + name: "59119" + id: 590 + display_name: "Pontia protodice" +} +item { + name: "18160" + id: 591 + display_name: "Melanerpes lewis" +} +item { + name: "18161" + id: 592 + display_name: "Melanerpes uropygialis" +} +item { + name: "50931" + id: 593 + display_name: "Strymon melinus" +} +item { + name: "59124" + id: 594 + display_name: "Anthocharis sara" +} +item { + name: "59127" + id: 595 + display_name: "Lycaena helloides" +} +item { + name: "59128" + id: 596 + display_name: "Atlides halesus" +} +item { + name: "67324" + id: 597 + display_name: "Eurema daira" +} +item { + name: "9981" + id: 598 + display_name: "Passerculus sandwichensis" +} +item { + name: "59134" + id: 599 + display_name: "Satyrium sylvinus" +} +item { + name: "67327" + id: 600 + display_name: "Schistocerca obscura" +} +item { + name: "67328" + id: 601 + display_name: "Pholcus phalangioides" +} +item { + name: "59138" + id: 602 + display_name: "Satyrium saepium" +} +item { + name: "132867" + id: 603 + display_name: "Microtia elva" +} +item { + name: "18181" + id: 604 + display_name: "Melanerpes pucherani" +} +item { + name: "7486" + id: 605 + display_name: "Salpinctes obsoletus" +} +item { + name: "108303" + id: 606 + display_name: "Paltothemis lineatipes" +} +item { + name: "59152" + id: 607 + display_name: "Leptotes marina" +} +item { + name: "132881" + id: 608 + display_name: "Catocala ultronia" +} +item { + name: "143662" + id: 609 + display_name: "Orthosoma brunneum" +} +item { + name: "59164" + id: 610 + display_name: "Plebejus icarioides" +} +item { + name: "18205" + id: 611 + display_name: "Melanerpes carolinus" +} +item { + name: "18206" + id: 612 + display_name: "Melanerpes chrysogenys" +} +item { + name: "83744" + id: 613 + display_name: "Amblyomma americanum" +} +item { + name: "18209" + id: 614 + display_name: "Melanerpes formicivorus" +} +item { + name: "116517" + id: 615 + display_name: "Caiman crocodilus" +} +item { + name: "59176" + id: 616 + display_name: "Phyciodes mylitta" +} +item { + name: "59182" + id: 617 + display_name: "Euphydryas editha" +} +item { + name: "43997" + id: 618 + display_name: "Myocastor coypus" +} +item { + name: "59185" + id: 619 + display_name: "Coenonympha tullia" +} +item { + name: "59187" + id: 620 + display_name: "Erynnis propertius" +} +item { + name: "59188" + id: 621 + display_name: "Erynnis funeralis" +} +item { + name: "59189" + id: 622 + display_name: "Erynnis tristis" +} +item { + name: "59190" + id: 623 + display_name: "Heliopetes ericetorum" +} +item { + name: "34615" + id: 624 + display_name: "Gekko gecko" +} +item { + name: "42808" + id: 625 + display_name: "Trichosurus vulpecula" +} +item { + name: "59194" + id: 626 + display_name: "Ochlodes sylvanoides" +} +item { + name: "59195" + id: 627 + display_name: "Lerodea eufala" +} +item { + name: "18236" + id: 628 + display_name: "Colaptes auratus" +} +item { + name: "10045" + id: 629 + display_name: "Basileuterus rufifrons" +} +item { + name: "59202" + id: 630 + display_name: "Larus michahellis" +} +item { + name: "10053" + id: 631 + display_name: "Ramphocelus passerinii" +} +item { + name: "19975" + id: 632 + display_name: "Athene cunicularia" +} +item { + name: "82231" + id: 633 + display_name: "Periplaneta americana" +} +item { + name: "67409" + id: 634 + display_name: "Gobiesox maeandricus" +} +item { + name: "83795" + id: 635 + display_name: "Cipangopaludina chinensis" +} +item { + name: "59220" + id: 636 + display_name: "Branta hutchinsii" +} +item { + name: "10069" + id: 637 + display_name: "Fringilla montifringilla" +} +item { + name: "10070" + id: 638 + display_name: "Fringilla coelebs" +} +item { + name: "83802" + id: 639 + display_name: "Megacyllene robiniae" +} +item { + name: "83804" + id: 640 + display_name: "Dynastes tityus" +} +item { + name: "51039" + id: 641 + display_name: "Cepaea hortensis" +} +item { + name: "68062" + id: 642 + display_name: "Menemerus bivittatus" +} +item { + name: "47527" + id: 643 + display_name: "Ostracion meleagris" +} +item { + name: "67435" + id: 644 + display_name: "Urbanus proteus" +} +item { + name: "10094" + id: 645 + display_name: "Junco hyemalis" +} +item { + name: "67440" + id: 646 + display_name: "Utetheisa ornatrix" +} +item { + name: "100210" + id: 647 + display_name: "Epitheca canis" +} +item { + name: "1907" + id: 648 + display_name: "Cuculus canorus" +} +item { + name: "100215" + id: 649 + display_name: "Epitheca princeps" +} +item { + name: "27826" + id: 650 + display_name: "Taricha granulosa" +} +item { + name: "129147" + id: 651 + display_name: "Ammophila procera" +} +item { + name: "10111" + id: 652 + display_name: "Junco phaeonotus" +} +item { + name: "83844" + id: 653 + display_name: "Oxyopes salticus" +} +item { + name: "144107" + id: 654 + display_name: "Tetracis crocallata" +} +item { + name: "51097" + id: 655 + display_name: "Papilio zelicaon" +} +item { + name: "10138" + id: 656 + display_name: "Ammodramus nelsoni" +} +item { + name: "10139" + id: 657 + display_name: "Ammodramus savannarum" +} +item { + name: "10147" + id: 658 + display_name: "Ammodramus maritimus" +} +item { + name: "59300" + id: 659 + display_name: "Anagrapha falcifera" +} +item { + name: "51110" + id: 660 + display_name: "Xylocopa virginica" +} +item { + name: "1960" + id: 661 + display_name: "Coccyzus erythropthalmus" +} +item { + name: "42652" + id: 662 + display_name: "Didelphis virginiana" +} +item { + name: "428606" + id: 663 + display_name: "Heraclides rumiko" +} +item { + name: "127303" + id: 664 + display_name: "Callophrys henrici" +} +item { + name: "1964" + id: 665 + display_name: "Coccyzus minor" +} +item { + name: "1965" + id: 666 + display_name: "Coccyzus americanus" +} +item { + name: "8520" + id: 667 + display_name: "Nucifraga columbiana" +} +item { + name: "116658" + id: 668 + display_name: "Siphanta acuta" +} +item { + name: "1972" + id: 669 + display_name: "Crotophaga sulcirostris" +} +item { + name: "10168" + id: 670 + display_name: "Pooecetes gramineus" +} +item { + name: "53893" + id: 671 + display_name: "Chlosyne palla" +} +item { + name: "10173" + id: 672 + display_name: "Arremonops rufivirgatus" +} +item { + name: "1986" + id: 673 + display_name: "Geococcyx californianus" +} +item { + name: "1987" + id: 674 + display_name: "Geococcyx velox" +} +item { + name: "116680" + id: 675 + display_name: "Tabanus atratus" +} +item { + name: "116681" + id: 676 + display_name: "Atteva aurea" +} +item { + name: "124875" + id: 677 + display_name: "Spodoptera litura" +} +item { + name: "26575" + id: 678 + display_name: "Diadophis punctatus" +} +item { + name: "10199" + id: 679 + display_name: "Coereba flaveola" +} +item { + name: "26591" + id: 680 + display_name: "Diadophis punctatus edwardsii" +} +item { + name: "59360" + id: 681 + display_name: "Neverita duplicata" +} +item { + name: "68263" + id: 682 + display_name: "Papilio multicaudata" +} +item { + name: "26598" + id: 683 + display_name: "Diadophis punctatus amabilis" +} +item { + name: "42983" + id: 684 + display_name: "Phascolarctos cinereus" +} +item { + name: "67560" + id: 685 + display_name: "Adelpha californica" +} +item { + name: "10224" + id: 686 + display_name: "Passerina ciris" +} +item { + name: "2038" + id: 687 + display_name: "Alectura lathami" +} +item { + name: "10232" + id: 688 + display_name: "Passerina leclancherii" +} +item { + name: "10234" + id: 689 + display_name: "Passerina amoena" +} +item { + name: "10243" + id: 690 + display_name: "Icteria virens" +} +item { + name: "2052" + id: 691 + display_name: "Crax rubra" +} +item { + name: "94551" + id: 692 + display_name: "Argia immunda" +} +item { + name: "2062" + id: 693 + display_name: "Penelope purpurascens" +} +item { + name: "204490" + id: 694 + display_name: "Copsychus malabaricus" +} +item { + name: "10257" + id: 695 + display_name: "Paroaria capitata" +} +item { + name: "51221" + id: 696 + display_name: "Procambarus clarkii" +} +item { + name: "10262" + id: 697 + display_name: "Cyanerpes cyaneus" +} +item { + name: "508249" + id: 698 + display_name: "Microcarbo melanoleucos brevirostris" +} +item { + name: "18460" + id: 699 + display_name: "Sphyrapicus thyroideus" +} +item { + name: "10271" + id: 700 + display_name: "Pheucticus ludovicianus" +} +item { + name: "18464" + id: 701 + display_name: "Sphyrapicus ruber" +} +item { + name: "10274" + id: 702 + display_name: "Pheucticus melanocephalus" +} +item { + name: "18467" + id: 703 + display_name: "Sphyrapicus nuchalis" +} +item { + name: "100391" + id: 704 + display_name: "Erythrodiplax berenice" +} +item { + name: "2089" + id: 705 + display_name: "Ortalis poliocephala" +} +item { + name: "2090" + id: 706 + display_name: "Ortalis vetula" +} +item { + name: "8038" + id: 707 + display_name: "Corvus albus" +} +item { + name: "67629" + id: 708 + display_name: "Oligocottus maculosus" +} +item { + name: "10286" + id: 709 + display_name: "Mniotilta varia" +} +item { + name: "10288" + id: 710 + display_name: "Volatinia jacarina" +} +item { + name: "100403" + id: 711 + display_name: "Erythrodiplax minuscula" +} +item { + name: "84023" + id: 712 + display_name: "Amorpha juglandis" +} +item { + name: "84024" + id: 713 + display_name: "Galasa nigrinodis" +} +item { + name: "10297" + id: 714 + display_name: "Thraupis palmarum" +} +item { + name: "67642" + id: 715 + display_name: "Pantherophis spiloides" +} +item { + name: "67653" + id: 716 + display_name: "Phoebis agarithe" +} +item { + name: "84038" + id: 717 + display_name: "Haploa lecontei" +} +item { + name: "26695" + id: 718 + display_name: "Scaphiopus holbrookii" +} +item { + name: "84040" + id: 719 + display_name: "Chauliognathus marginatus" +} +item { + name: "51275" + id: 720 + display_name: "Pentatoma rufipes" +} +item { + name: "2124" + id: 721 + display_name: "Momotus mexicanus" +} +item { + name: "26702" + id: 722 + display_name: "Spea hammondii" +} +item { + name: "10325" + id: 723 + display_name: "Euphagus cyanocephalus" +} +item { + name: "43102" + id: 724 + display_name: "Sylvilagus palustris" +} +item { + name: "49509" + id: 725 + display_name: "Lutjanus griseus" +} +item { + name: "116834" + id: 726 + display_name: "Cacatua galerita" +} +item { + name: "127188" + id: 727 + display_name: "Junco hyemalis oreganus" +} +item { + name: "26725" + id: 728 + display_name: "Ambystoma jeffersonianum" +} +item { + name: "43111" + id: 729 + display_name: "Sylvilagus floridanus" +} +item { + name: "43112" + id: 730 + display_name: "Sylvilagus bachmani" +} +item { + name: "67691" + id: 731 + display_name: "Lophocampa maculata" +} +item { + name: "51311" + id: 732 + display_name: "Urbanus dorantes" +} +item { + name: "67700" + id: 733 + display_name: "Caracolus caracolla" +} +item { + name: "43128" + id: 734 + display_name: "Lepus europaeus" +} +item { + name: "26745" + id: 735 + display_name: "Ambystoma texanum" +} +item { + name: "67706" + id: 736 + display_name: "Argiope argentata" +} +item { + name: "26747" + id: 737 + display_name: "Ambystoma gracile" +} +item { + name: "67708" + id: 738 + display_name: "Argiope trifasciata" +} +item { + name: "26749" + id: 739 + display_name: "Ambystoma tigrinum" +} +item { + name: "4896" + id: 740 + display_name: "Pluvialis fulva" +} +item { + name: "10369" + id: 741 + display_name: "Molothrus aeneus" +} +item { + name: "26754" + id: 742 + display_name: "Ambystoma macrodactylum" +} +item { + name: "10373" + id: 743 + display_name: "Molothrus ater" +} +item { + name: "2185" + id: 744 + display_name: "Merops pusillus" +} +item { + name: "84109" + id: 745 + display_name: "Pisaurina mira" +} +item { + name: "67726" + id: 746 + display_name: "Aeshna palmata" +} +item { + name: "2191" + id: 747 + display_name: "Merops apiaster" +} +item { + name: "67731" + id: 748 + display_name: "Anax junius" +} +item { + name: "198804" + id: 749 + display_name: "Satyrium titus" +} +item { + name: "51349" + id: 750 + display_name: "Pyrgus communis" +} +item { + name: "18584" + id: 751 + display_name: "Pteroglossus torquatus" +} +item { + name: "67737" + id: 752 + display_name: "Rhionaeschna multicolor" +} +item { + name: "198812" + id: 753 + display_name: "Lethe anthedon" +} +item { + name: "321697" + id: 754 + display_name: "Melanchroia chephise" +} +item { + name: "198821" + id: 755 + display_name: "Pieris oleracea" +} +item { + name: "26790" + id: 756 + display_name: "Ambystoma maculatum" +} +item { + name: "10411" + id: 757 + display_name: "Loxia curvirostra" +} +item { + name: "133295" + id: 758 + display_name: "Melitaea didyma" +} +item { + name: "67760" + id: 759 + display_name: "Popillia japonica" +} +item { + name: "43188" + id: 760 + display_name: "Ochotona princeps" +} +item { + name: "2229" + id: 761 + display_name: "Merops orientalis" +} +item { + name: "10423" + id: 762 + display_name: "Loxia leucoptera" +} +item { + name: "67771" + id: 763 + display_name: "Leptoglossus occidentalis" +} +item { + name: "84162" + id: 764 + display_name: "Chrysochus auratus" +} +item { + name: "26822" + id: 765 + display_name: "Dicamptodon tenebrosus" +} +item { + name: "26823" + id: 766 + display_name: "Dicamptodon ensatus" +} +item { + name: "51402" + id: 767 + display_name: "Megalops atlanticus" +} +item { + name: "67725" + id: 768 + display_name: "Aeshna interrupta" +} +item { + name: "411858" + id: 769 + display_name: "Vanessa gonerilla gonerilla" +} +item { + name: "26835" + id: 770 + display_name: "Drymobius margaritiferus" +} +item { + name: "84185" + id: 771 + display_name: "Megalopyge opercularis" +} +item { + name: "2266" + id: 772 + display_name: "Coracias garrulus" +} +item { + name: "141531" + id: 773 + display_name: "Lethe eurydice" +} +item { + name: "2269" + id: 774 + display_name: "Coracias caudatus" +} +item { + name: "133346" + id: 775 + display_name: "Melittia cucurbitae" +} +item { + name: "2275" + id: 776 + display_name: "Coracias benghalensis" +} +item { + name: "84196" + id: 777 + display_name: "Pontania californica" +} +item { + name: "10470" + id: 778 + display_name: "Xanthocephalus xanthocephalus" +} +item { + name: "10479" + id: 779 + display_name: "Chondestes grammacus" +} +item { + name: "51440" + id: 780 + display_name: "Pituophis catenifer catenifer" +} +item { + name: "54087" + id: 781 + display_name: "Pieris napi" +} +item { + name: "59635" + id: 782 + display_name: "Phragmatopoma californica" +} +item { + name: "10487" + id: 783 + display_name: "Dolichonyx oryzivorus" +} +item { + name: "67835" + id: 784 + display_name: "Danaus chrysippus" +} +item { + name: "59644" + id: 785 + display_name: "Pantherophis alleghaniensis" +} +item { + name: "59646" + id: 786 + display_name: "Pantherophis bairdi" +} +item { + name: "116999" + id: 787 + display_name: "Pandion haliaetus" +} +item { + name: "117002" + id: 788 + display_name: "Phainopepla nitens" +} +item { + name: "16770" + id: 789 + display_name: "Tyrannus couchii" +} +item { + name: "84239" + id: 790 + display_name: "Callophrys gryneus" +} +item { + name: "104553" + id: 791 + display_name: "Leucorrhinia proxima" +} +item { + name: "117016" + id: 792 + display_name: "Phylloscopus collybita" +} +item { + name: "49540" + id: 793 + display_name: "Gasteracantha cancriformis" +} +item { + name: "59675" + id: 794 + display_name: "Pyrrharctia isabella" +} +item { + name: "469277" + id: 795 + display_name: "Neotibicen superbus" +} +item { + name: "236973" + id: 796 + display_name: "Circus cyaneus hudsonius" +} +item { + name: "59683" + id: 797 + display_name: "Porpita porpita" +} +item { + name: "26916" + id: 798 + display_name: "Contia tenuis" +} +item { + name: "51493" + id: 799 + display_name: "Trimerotropis pallidipennis" +} +item { + name: "51495" + id: 800 + display_name: "Anthocharis cardamines" +} +item { + name: "133416" + id: 801 + display_name: "Phoebis philea" +} +item { + name: "8583" + id: 802 + display_name: "Grallina cyanoleuca" +} +item { + name: "395569" + id: 803 + display_name: "Prionoplus reticularis" +} +item { + name: "59698" + id: 804 + display_name: "Velella velella" +} +item { + name: "141626" + id: 805 + display_name: "Lygaeus turcicus" +} +item { + name: "84286" + id: 806 + display_name: "Diapheromera femorata" +} +item { + name: "117059" + id: 807 + display_name: "Plectrophenax nivalis" +} +item { + name: "133447" + id: 808 + display_name: "Crambus agitatellus" +} +item { + name: "133448" + id: 809 + display_name: "Climaciella brunnea" +} +item { + name: "51534" + id: 810 + display_name: "Leptotes cassius" +} +item { + name: "205197" + id: 811 + display_name: "Eutrapela clemataria" +} +item { + name: "51536" + id: 812 + display_name: "Ascia monuste" +} +item { + name: "10585" + id: 813 + display_name: "Calamospiza melanocorys" +} +item { + name: "49552" + id: 814 + display_name: "Scutigera coleoptrata" +} +item { + name: "51555" + id: 815 + display_name: "Sympetrum illotum" +} +item { + name: "51557" + id: 816 + display_name: "Bombylius major" +} +item { + name: "117095" + id: 817 + display_name: "Regulus calendula" +} +item { + name: "117097" + id: 818 + display_name: "Regulus ignicapilla" +} +item { + name: "117099" + id: 819 + display_name: "Regulus regulus" +} +item { + name: "117100" + id: 820 + display_name: "Regulus satrapa" +} +item { + name: "84333" + id: 821 + display_name: "Eudryas grata" +} +item { + name: "215409" + id: 822 + display_name: "Bradybaena similaris" +} +item { + name: "16787" + id: 823 + display_name: "Tyrannus melancholicus" +} +item { + name: "46225" + id: 824 + display_name: "Tamias dorsalis" +} +item { + name: "59774" + id: 825 + display_name: "Pachydiplax longipennis" +} +item { + name: "59776" + id: 826 + display_name: "Perithemis tenera" +} +item { + name: "119014" + id: 827 + display_name: "Argia fumipennis violacea" +} +item { + name: "4326" + id: 828 + display_name: "Pelecanus conspicillatus" +} +item { + name: "18833" + id: 829 + display_name: "Aulacorhynchus prasinus" +} +item { + name: "43411" + id: 830 + display_name: "Ateles geoffroyi" +} +item { + name: "141725" + id: 831 + display_name: "Nezara viridula" +} +item { + name: "51614" + id: 832 + display_name: "Eurema hecabe" +} +item { + name: "125343" + id: 833 + display_name: "Crepidula fornicata" +} +item { + name: "2464" + id: 834 + display_name: "Todiramphus sanctus" +} +item { + name: "43432" + id: 835 + display_name: "Cebus capucinus" +} +item { + name: "43436" + id: 836 + display_name: "Alouatta palliata" +} +item { + name: "43439" + id: 837 + display_name: "Alouatta pigra" +} +item { + name: "9357" + id: 838 + display_name: "Icterus bullockii" +} +item { + name: "84403" + id: 839 + display_name: "Phyllopalpus pulchellus" +} +item { + name: "10676" + id: 840 + display_name: "Spiza americana" +} +item { + name: "16798" + id: 841 + display_name: "Tyrannus dominicensis" +} +item { + name: "141752" + id: 842 + display_name: "Biblis hyperia" +} +item { + name: "4512" + id: 843 + display_name: "Chlidonias niger" +} +item { + name: "43460" + id: 844 + display_name: "Macaca mulatta" +} +item { + name: "51654" + id: 845 + display_name: "Junonia almana" +} +item { + name: "51659" + id: 846 + display_name: "Anthopleura xanthogrammica" +} +item { + name: "84428" + id: 847 + display_name: "Drepana arcuata" +} +item { + name: "10702" + id: 848 + display_name: "Oriturus superciliosus" +} +item { + name: "68047" + id: 849 + display_name: "Psarocolius montezuma" +} +item { + name: "12707" + id: 850 + display_name: "Turdus pilaris" +} +item { + name: "84437" + id: 851 + display_name: "Nicrophorus orbicollis" +} +item { + name: "84438" + id: 852 + display_name: "Platyprepia virginalis" +} +item { + name: "117209" + id: 853 + display_name: "Notiomystis cincta" +} +item { + name: "343393" + id: 854 + display_name: "Hypsopygia olinalis" +} +item { + name: "27101" + id: 855 + display_name: "Eurycea longicauda" +} +item { + name: "117214" + id: 856 + display_name: "Sagittarius serpentarius" +} +item { + name: "18911" + id: 857 + display_name: "Psittacula krameri" +} +item { + name: "117218" + id: 858 + display_name: "Verrucosa arenata" +} +item { + name: "117221" + id: 859 + display_name: "Dasymutilla occidentalis" +} +item { + name: "35303" + id: 860 + display_name: "Ctenosaura similis" +} +item { + name: "18920" + id: 861 + display_name: "Platycercus eximius" +} +item { + name: "10729" + id: 862 + display_name: "Protonotaria citrea" +} +item { + name: "35306" + id: 863 + display_name: "Ctenosaura pectinata" +} +item { + name: "109650" + id: 864 + display_name: "Platycnemis pennipes" +} +item { + name: "27120" + id: 865 + display_name: "Eurycea bislineata" +} +item { + name: "27123" + id: 866 + display_name: "Eurycea lucifuga" +} +item { + name: "51702" + id: 867 + display_name: "Coccinella septempunctata" +} +item { + name: "2552" + id: 868 + display_name: "Megaceryle torquata" +} +item { + name: "133625" + id: 869 + display_name: "Zanclognatha jacchusalis" +} +item { + name: "18943" + id: 870 + display_name: "Nestor meridionalis" +} +item { + name: "84481" + id: 871 + display_name: "Calopteryx maculata" +} +item { + name: "35330" + id: 872 + display_name: "Sauromalus ater" +} +item { + name: "27140" + id: 873 + display_name: "Coluber constrictor priapus" +} +item { + name: "199179" + id: 874 + display_name: "Polistes chinensis" +} +item { + name: "51724" + id: 875 + display_name: "Mopalia lignosa" +} +item { + name: "27149" + id: 876 + display_name: "Coluber constrictor constrictor" +} +item { + name: "35342" + id: 877 + display_name: "Iguana iguana" +} +item { + name: "27153" + id: 878 + display_name: "Coluber constrictor flaviventris" +} +item { + name: "35347" + id: 879 + display_name: "Amblyrhynchus cristatus" +} +item { + name: "125461" + id: 880 + display_name: "Ursus arctos horribilis" +} +item { + name: "84507" + id: 881 + display_name: "Lygus lineolaris" +} +item { + name: "35356" + id: 882 + display_name: "Dipsosaurus dorsalis" +} +item { + name: "51743" + id: 883 + display_name: "Danaus gilippus" +} +item { + name: "18976" + id: 884 + display_name: "Amazona viridigenalis" +} +item { + name: "125475" + id: 885 + display_name: "Plusiodonta compressipalpis" +} +item { + name: "51748" + id: 886 + display_name: "Danaus gilippus thersippus" +} +item { + name: "68137" + id: 887 + display_name: "Chlorocebus pygerythrus" +} +item { + name: "133675" + id: 888 + display_name: "Coenobita clypeatus" +} +item { + name: "215596" + id: 889 + display_name: "Buprestis aurulenta" +} +item { + name: "117293" + id: 890 + display_name: "Oecophylla smaragdina" +} +item { + name: "68142" + id: 891 + display_name: "Prenolepis imparis" +} +item { + name: "27184" + id: 892 + display_name: "Plethodon glutinosus" +} +item { + name: "27186" + id: 893 + display_name: "Plethodon cinereus" +} +item { + name: "18995" + id: 894 + display_name: "Amazona albifrons" +} +item { + name: "51765" + id: 895 + display_name: "Poanes melane" +} +item { + name: "18998" + id: 896 + display_name: "Amazona oratrix" +} +item { + name: "41396" + id: 897 + display_name: "Rhynchonycteris naso" +} +item { + name: "27194" + id: 898 + display_name: "Plethodon vehiculum" +} +item { + name: "51773" + id: 899 + display_name: "Nathalis iole" +} +item { + name: "12908" + id: 900 + display_name: "Saxicola rubetra" +} +item { + name: "68165" + id: 901 + display_name: "Linepithema humile" +} +item { + name: "154721" + id: 902 + display_name: "Brachygastra mellifica" +} +item { + name: "338504" + id: 903 + display_name: "Xanthocnemis zealandica" +} +item { + name: "338505" + id: 904 + display_name: "Melangyna novaezelandiae" +} +item { + name: "27093" + id: 905 + display_name: "Eurycea cirrigera" +} +item { + name: "65975" + id: 906 + display_name: "Lithobates berlandieri" +} +item { + name: "19020" + id: 907 + display_name: "Ara militaris" +} +item { + name: "474210" + id: 908 + display_name: "Spizelloides arborea" +} +item { + name: "205240" + id: 909 + display_name: "Pantographa limata" +} +item { + name: "27226" + id: 910 + display_name: "Plethodon albagula" +} +item { + name: "318545" + id: 911 + display_name: "Coreus marginatus" +} +item { + name: "2662" + id: 912 + display_name: "Ceryle rudis" +} +item { + name: "109161" + id: 913 + display_name: "Perithemis intensa" +} +item { + name: "51824" + id: 914 + display_name: "Calopteryx splendens" +} +item { + name: "27250" + id: 915 + display_name: "Ensatina eschscholtzii" +} +item { + name: "2676" + id: 916 + display_name: "Chloroceryle aenea" +} +item { + name: "2679" + id: 917 + display_name: "Chloroceryle amazona" +} +item { + name: "84602" + id: 918 + display_name: "Zale lunata" +} +item { + name: "133756" + id: 919 + display_name: "Leptoglossus oppositus" +} +item { + name: "35453" + id: 920 + display_name: "Zootoca vivipara" +} +item { + name: "84612" + id: 921 + display_name: "Polyphylla decemlineata" +} +item { + name: "133765" + id: 922 + display_name: "Eumenes fraternus" +} +item { + name: "68230" + id: 923 + display_name: "Brachymesia gravida" +} +item { + name: "49601" + id: 924 + display_name: "Mola mola" +} +item { + name: "68232" + id: 925 + display_name: "Papilio palamedes" +} +item { + name: "68233" + id: 926 + display_name: "Orthemis ferruginea" +} +item { + name: "68239" + id: 927 + display_name: "Parnassius clodius" +} +item { + name: "68240" + id: 928 + display_name: "Chlosyne lacinia" +} +item { + name: "68244" + id: 929 + display_name: "Euptoieta claudia" +} +item { + name: "68249" + id: 930 + display_name: "Dymasia dymas" +} +item { + name: "68251" + id: 931 + display_name: "Limenitis weidemeyerii" +} +item { + name: "133790" + id: 932 + display_name: "Chalybion californicum" +} +item { + name: "84644" + id: 933 + display_name: "Phalangium opilio" +} +item { + name: "68262" + id: 934 + display_name: "Polygonia faunus" +} +item { + name: "133799" + id: 935 + display_name: "Xenox tigrinus" +} +item { + name: "68264" + id: 936 + display_name: "Asterocampa celtis" +} +item { + name: "132892" + id: 937 + display_name: "Anacridium aegyptium" +} +item { + name: "68268" + id: 938 + display_name: "Euptoieta hegesia" +} +item { + name: "68269" + id: 939 + display_name: "Aglais milberti" +} +item { + name: "43694" + id: 940 + display_name: "Loxodonta africana" +} +item { + name: "59165" + id: 941 + display_name: "Apodemia mormo" +} +item { + name: "68274" + id: 942 + display_name: "Phyciodes phaon" +} +item { + name: "68275" + id: 943 + display_name: "Battus polydamas" +} +item { + name: "84662" + id: 944 + display_name: "Celastrina lucia" +} +item { + name: "16842" + id: 945 + display_name: "Myiozetetes similis" +} +item { + name: "133826" + id: 946 + display_name: "Zelus longipes" +} +item { + name: "14912" + id: 947 + display_name: "Toxostoma curvirostre" +} +item { + name: "53708" + id: 948 + display_name: "Pacifastacus leniusculus" +} +item { + name: "117452" + id: 949 + display_name: "Sphinx kalmiae" +} +item { + name: "182997" + id: 950 + display_name: "Megisto rubricata" +} +item { + name: "223965" + id: 951 + display_name: "Lithacodia musta" +} +item { + name: "125663" + id: 952 + display_name: "Kelletia kelletii" +} +item { + name: "125669" + id: 953 + display_name: "Rumina decollata" +} +item { + name: "68328" + id: 954 + display_name: "Oxythyrea funesta" +} +item { + name: "179324" + id: 955 + display_name: "Dactylotum bicolor" +} +item { + name: "68330" + id: 956 + display_name: "Arctia caja" +} +item { + name: "2548" + id: 957 + display_name: "Megaceryle alcyon" +} +item { + name: "207600" + id: 958 + display_name: "Thasus neocalifornicus" +} +item { + name: "207601" + id: 959 + display_name: "Palpita quadristigmalis" +} +item { + name: "51954" + id: 960 + display_name: "Sphecius speciosus" +} +item { + name: "207603" + id: 961 + display_name: "Prolimacodes badia" +} +item { + name: "7294" + id: 962 + display_name: "Eremophila alpestris" +} +item { + name: "19196" + id: 963 + display_name: "Alisterus scapularis" +} +item { + name: "145194" + id: 964 + display_name: "Cinnyris jugularis" +} +item { + name: "27390" + id: 965 + display_name: "Desmognathus ochrophaeus" +} +item { + name: "207615" + id: 966 + display_name: "Polistes apachus" +} +item { + name: "63275" + id: 967 + display_name: "Tremex columba" +} +item { + name: "61910" + id: 968 + display_name: "Orgyia antiqua" +} +item { + name: "199438" + id: 969 + display_name: "Orgyia postica" +} +item { + name: "43794" + id: 970 + display_name: "Castor canadensis" +} +item { + name: "84755" + id: 971 + display_name: "Arion rufus" +} +item { + name: "51996" + id: 972 + display_name: "Daphnis nerii" +} +item { + name: "194075" + id: 973 + display_name: "Drymarchon melanurus erebennus" +} +item { + name: "133923" + id: 974 + display_name: "Mermiria bivittata" +} +item { + name: "84778" + id: 975 + display_name: "Leptinotarsa decemlineata" +} +item { + name: "11051" + id: 976 + display_name: "Xiphorhynchus flavigaster" +} +item { + name: "121992" + id: 977 + display_name: "Cervus elaphus roosevelti" +} +item { + name: "27459" + id: 978 + display_name: "Batrachoseps attenuatus" +} +item { + name: "84806" + id: 979 + display_name: "Acanalonia conica" +} +item { + name: "52043" + id: 980 + display_name: "Spoladea recurvalis" +} +item { + name: "27468" + id: 981 + display_name: "Batrachoseps major" +} +item { + name: "133966" + id: 982 + display_name: "Lomographa vestaliata" +} +item { + name: "27474" + id: 983 + display_name: "Batrachoseps nigriventris" +} +item { + name: "101204" + id: 984 + display_name: "Gambusia holbrooki" +} +item { + name: "52055" + id: 985 + display_name: "Crocothemis servilia" +} +item { + name: "4580" + id: 986 + display_name: "Jacana jacana" +} +item { + name: "346970" + id: 987 + display_name: "Callophrys dumetorum" +} +item { + name: "27486" + id: 988 + display_name: "Pseudotriton ruber" +} +item { + name: "52075" + id: 989 + display_name: "Atalopedes campestris" +} +item { + name: "27500" + id: 990 + display_name: "Gyrinophilus porphyriticus" +} +item { + name: "73203" + id: 991 + display_name: "Phalaropus fulicarius" +} +item { + name: "322417" + id: 992 + display_name: "Limacus flavus" +} +item { + name: "40083" + id: 993 + display_name: "Gopherus berlandieri" +} +item { + name: "68469" + id: 994 + display_name: "Papilio demodocus" +} +item { + name: "2938" + id: 995 + display_name: "Streptopelia turtur" +} +item { + name: "117633" + id: 996 + display_name: "Mopalia muscosa" +} +item { + name: "117641" + id: 997 + display_name: "Nucella lamellosa" +} +item { + name: "322443" + id: 998 + display_name: "Thasus gigas" +} +item { + name: "68492" + id: 999 + display_name: "Hemidactylus mabouia" +} +item { + name: "143853" + id: 1000 + display_name: "Pica hudsonia" +} +item { + name: "144757" + id: 1001 + display_name: "Corvus cornix" +} +item { + name: "117650" + id: 1002 + display_name: "Mytilus edulis" +} +item { + name: "19349" + id: 1003 + display_name: "Myiopsitta monachus" +} +item { + name: "2969" + id: 1004 + display_name: "Streptopelia decaocto" +} +item { + name: "9919" + id: 1005 + display_name: "Piranga ludoviciana" +} +item { + name: "5009" + id: 1006 + display_name: "Ixobrychus exilis" +} +item { + name: "117666" + id: 1007 + display_name: "Pleuroncodes planipes" +} +item { + name: "7603" + id: 1008 + display_name: "Auriparus flaviceps" +} +item { + name: "117674" + id: 1009 + display_name: "Ligia occidentalis" +} +item { + name: "145223" + id: 1010 + display_name: "Geothlypis tolmiei" +} +item { + name: "60341" + id: 1011 + display_name: "Lithobates sphenocephalus" +} +item { + name: "60342" + id: 1012 + display_name: "Thamnophis proximus" +} +item { + name: "52155" + id: 1013 + display_name: "Dermacentor variabilis" +} +item { + name: "60349" + id: 1014 + display_name: "Scincella lateralis" +} +item { + name: "52158" + id: 1015 + display_name: "Schistocerca nitens" +} +item { + name: "117696" + id: 1016 + display_name: "Dendraster excentricus" +} +item { + name: "232391" + id: 1017 + display_name: "Tetracha carolina" +} +item { + name: "3017" + id: 1018 + display_name: "Columba livia" +} +item { + name: "145229" + id: 1019 + display_name: "Setophaga citrina" +} +item { + name: "84950" + id: 1020 + display_name: "Alypia octomaculata" +} +item { + name: "52188" + id: 1021 + display_name: "Rhincodon typus" +} +item { + name: "494559" + id: 1022 + display_name: "Polydrusus formosus" +} +item { + name: "145232" + id: 1023 + display_name: "Setophaga cerulea" +} +item { + name: "3048" + id: 1024 + display_name: "Columba palumbus" +} +item { + name: "9922" + id: 1025 + display_name: "Piranga bidentata" +} +item { + name: "44026" + id: 1026 + display_name: "Erethizon dorsatum" +} +item { + name: "61505" + id: 1027 + display_name: "Manduca sexta" +} +item { + name: "84994" + id: 1028 + display_name: "Acanthocephala declivis" +} +item { + name: "27652" + id: 1029 + display_name: "Hemidactylium scutatum" +} +item { + name: "117767" + id: 1030 + display_name: "Cervus elaphus nannodes" +} +item { + name: "494603" + id: 1031 + display_name: "Hermissenda opalescens" +} +item { + name: "39819" + id: 1032 + display_name: "Terrapene carolina bauri" +} +item { + name: "3093" + id: 1033 + display_name: "Patagioenas leucocephala" +} +item { + name: "205316" + id: 1034 + display_name: "Aidemona azteca" +} +item { + name: "216093" + id: 1035 + display_name: "Caracolus marginella" +} +item { + name: "44062" + id: 1036 + display_name: "Thomomys bottae" +} +item { + name: "85024" + id: 1037 + display_name: "Heraclides cresphontes" +} +item { + name: "3108" + id: 1038 + display_name: "Patagioenas fasciata" +} +item { + name: "213510" + id: 1039 + display_name: "Anageshna primordialis" +} +item { + name: "85030" + id: 1040 + display_name: "Crocothemis erythraea" +} +item { + name: "85034" + id: 1041 + display_name: "Neoscona crucifera" +} +item { + name: "3117" + id: 1042 + display_name: "Patagioenas flavirostris" +} +item { + name: "207924" + id: 1043 + display_name: "Synchlora frondaria" +} +item { + name: "35900" + id: 1044 + display_name: "Lacerta bilineata" +} +item { + name: "24382" + id: 1045 + display_name: "Osteopilus septentrionalis" +} +item { + name: "145249" + id: 1046 + display_name: "Setophaga discolor" +} +item { + name: "52297" + id: 1047 + display_name: "Triakis semifasciata" +} +item { + name: "27726" + id: 1048 + display_name: "Salamandra salamandra" +} +item { + name: "27727" + id: 1049 + display_name: "Bogertophis subocularis" +} +item { + name: "143043" + id: 1050 + display_name: "Cycnia tenera" +} +item { + name: "52313" + id: 1051 + display_name: "Diodon hystrix" +} +item { + name: "143316" + id: 1052 + display_name: "Schinia florida" +} +item { + name: "61968" + id: 1053 + display_name: "Graphosoma lineatum" +} +item { + name: "502885" + id: 1054 + display_name: "Lissachatina fulica" +} +item { + name: "71029" + id: 1055 + display_name: "Crotalus cerastes cerastes" +} +item { + name: "207977" + id: 1056 + display_name: "Aglais io" +} +item { + name: "19577" + id: 1057 + display_name: "Chordeiles minor" +} +item { + name: "93312" + id: 1058 + display_name: "Acropora palmata" +} +item { + name: "52354" + id: 1059 + display_name: "Ambystoma laterale" +} +item { + name: "19587" + id: 1060 + display_name: "Chordeiles acutipennis" +} +item { + name: "58585" + id: 1061 + display_name: "Limenitis arthemis astyanax" +} +item { + name: "134277" + id: 1062 + display_name: "Gastrophryne olivacea" +} +item { + name: "60551" + id: 1063 + display_name: "Papilio glaucus" +} +item { + name: "3731" + id: 1064 + display_name: "Platalea leucorodia" +} +item { + name: "232593" + id: 1065 + display_name: "Thyris sepulchralis" +} +item { + name: "19609" + id: 1066 + display_name: "Phalaenoptilus nuttallii" +} +item { + name: "126106" + id: 1067 + display_name: "Haploa clymene" +} +item { + name: "27805" + id: 1068 + display_name: "Notophthalmus viridescens" +} +item { + name: "199840" + id: 1069 + display_name: "Haemorhous mexicanus" +} +item { + name: "199841" + id: 1070 + display_name: "Haemorhous purpureus" +} +item { + name: "219719" + id: 1071 + display_name: "Eudryas unio" +} +item { + name: "27818" + id: 1072 + display_name: "Taricha torosa" +} +item { + name: "19627" + id: 1073 + display_name: "Nyctidromus albicollis" +} +item { + name: "28750" + id: 1074 + display_name: "Salvadora grahamiae lineata" +} +item { + name: "27824" + id: 1075 + display_name: "Taricha rivularis" +} +item { + name: "146632" + id: 1076 + display_name: "Toxomerus politus" +} +item { + name: "52402" + id: 1077 + display_name: "Cetonia aurata" +} +item { + name: "18291" + id: 1078 + display_name: "Campephilus guatemalensis" +} +item { + name: "60598" + id: 1079 + display_name: "Ixodes scapularis" +} +item { + name: "199870" + id: 1080 + display_name: "Pyralis farinalis" +} +item { + name: "60607" + id: 1081 + display_name: "Limenitis arthemis" +} +item { + name: "205241" + id: 1082 + display_name: "Plagodis phlogosaria" +} +item { + name: "14898" + id: 1083 + display_name: "Toxostoma rufum" +} +item { + name: "126153" + id: 1084 + display_name: "Amphion floridensis" +} +item { + name: "126155" + id: 1085 + display_name: "Vespula germanica" +} +item { + name: "51392" + id: 1086 + display_name: "Morone saxatilis" +} +item { + name: "3280" + id: 1087 + display_name: "Leptotila verreauxi" +} +item { + name: "19670" + id: 1088 + display_name: "Nyctibius jamaicensis" +} +item { + name: "6929" + id: 1089 + display_name: "Anas penelope" +} +item { + name: "97738" + id: 1090 + display_name: "Chromagrion conditum" +} +item { + name: "52449" + id: 1091 + display_name: "Rhinecanthus rectangulus" +} +item { + name: "52451" + id: 1092 + display_name: "Naso lituratus" +} +item { + name: "56529" + id: 1093 + display_name: "Papilio machaon" +} +item { + name: "199913" + id: 1094 + display_name: "Buteo plagiatus" +} +item { + name: "199914" + id: 1095 + display_name: "Selasphorus calliope" +} +item { + name: "85227" + id: 1096 + display_name: "Hemideina crassidens" +} +item { + name: "36076" + id: 1097 + display_name: "Cophosaurus texanus" +} +item { + name: "36077" + id: 1098 + display_name: "Cophosaurus texanus texanus" +} +item { + name: "208112" + id: 1099 + display_name: "Palpita magniferalis" +} +item { + name: "85235" + id: 1100 + display_name: "Deinacrida rugosa" +} +item { + name: "93429" + id: 1101 + display_name: "Aeshna constricta" +} +item { + name: "36086" + id: 1102 + display_name: "Callisaurus draconoides rhodostictus" +} +item { + name: "126204" + id: 1103 + display_name: "Synchlora aerata" +} +item { + name: "93437" + id: 1104 + display_name: "Aeshna mixta" +} +item { + name: "126207" + id: 1105 + display_name: "Schizura unicornis" +} +item { + name: "126209" + id: 1106 + display_name: "Metcalfa pruinosa" +} +item { + name: "126211" + id: 1107 + display_name: "Poecilocapsus lineatus" +} +item { + name: "36100" + id: 1108 + display_name: "Uta stansburiana elegans" +} +item { + name: "48342" + id: 1109 + display_name: "Hemigrapsus nudus" +} +item { + name: "199942" + id: 1110 + display_name: "Strategus aloeus" +} +item { + name: "126215" + id: 1111 + display_name: "Monobia quadridens" +} +item { + name: "101640" + id: 1112 + display_name: "Gomphaeschna furcillata" +} +item { + name: "126217" + id: 1113 + display_name: "Pyrausta orphisalis" +} +item { + name: "36107" + id: 1114 + display_name: "Urosaurus ornatus" +} +item { + name: "51940" + id: 1115 + display_name: "Hemidactylus frenatus" +} +item { + name: "36121" + id: 1116 + display_name: "Urosaurus graciosus" +} +item { + name: "19743" + id: 1117 + display_name: "Megascops kennicottii" +} +item { + name: "68901" + id: 1118 + display_name: "Salticus scenicus" +} +item { + name: "44326" + id: 1119 + display_name: "Microtus californicus" +} +item { + name: "82481" + id: 1120 + display_name: "Pieris marginalis" +} +item { + name: "474332" + id: 1121 + display_name: "Porphyrio poliocephalus" +} +item { + name: "81674" + id: 1122 + display_name: "Rivula propinqualis" +} +item { + name: "126252" + id: 1123 + display_name: "Mastigoproctus giganteus" +} +item { + name: "36142" + id: 1124 + display_name: "Sceloporus undulatus" +} +item { + name: "68911" + id: 1125 + display_name: "Libellula needhami" +} +item { + name: "68912" + id: 1126 + display_name: "Dysdera crocata" +} +item { + name: "42888" + id: 1127 + display_name: "Macropus giganteus" +} +item { + name: "19765" + id: 1128 + display_name: "Megascops asio" +} +item { + name: "68918" + id: 1129 + display_name: "Poecilanthrax lucifer" +} +item { + name: "333705" + id: 1130 + display_name: "Pantherophis obsoletus lindheimeri" +} +item { + name: "126267" + id: 1131 + display_name: "Coleomegilla maculata" +} +item { + name: "101693" + id: 1132 + display_name: "Gomphus vastus" +} +item { + name: "85221" + id: 1133 + display_name: "Hemideina thoracica" +} +item { + name: "126276" + id: 1134 + display_name: "Agrotis ipsilon" +} +item { + name: "85317" + id: 1135 + display_name: "Eurosta solidaginis" +} +item { + name: "36169" + id: 1136 + display_name: "Sceloporus spinosus" +} +item { + name: "60752" + id: 1137 + display_name: "Hermeuptychia sosybius" +} +item { + name: "60754" + id: 1138 + display_name: "Pyromorpha dimidiata" +} +item { + name: "126291" + id: 1139 + display_name: "Prosapia bicincta" +} +item { + name: "52564" + id: 1140 + display_name: "Anthopleura elegantissima" +} +item { + name: "126293" + id: 1141 + display_name: "Prionoxystus robiniae" +} +item { + name: "120719" + id: 1142 + display_name: "Pseudacris hypochondriaca" +} +item { + name: "36189" + id: 1143 + display_name: "Sceloporus poinsettii" +} +item { + name: "52576" + id: 1144 + display_name: "Uroctonus mordax" +} +item { + name: "36198" + id: 1145 + display_name: "Sceloporus orcutti" +} +item { + name: "52584" + id: 1146 + display_name: "Pantala hymenaea" +} +item { + name: "44395" + id: 1147 + display_name: "Peromyscus leucopus" +} +item { + name: "36204" + id: 1148 + display_name: "Sceloporus occidentalis" +} +item { + name: "52589" + id: 1149 + display_name: "Coenonympha pamphilus" +} +item { + name: "3439" + id: 1150 + display_name: "Zenaida auriculata" +} +item { + name: "36208" + id: 1151 + display_name: "Sceloporus occidentalis bocourtii" +} +item { + name: "72936" + id: 1152 + display_name: "Hymenolaimus malacorhynchos" +} +item { + name: "85362" + id: 1153 + display_name: "Sphex ichneumoneus" +} +item { + name: "36217" + id: 1154 + display_name: "Sceloporus merriami" +} +item { + name: "68993" + id: 1155 + display_name: "Liometopum occidentale" +} +item { + name: "199916" + id: 1156 + display_name: "Setophaga caerulescens" +} +item { + name: "52620" + id: 1157 + display_name: "Cicindela oregona" +} +item { + name: "36243" + id: 1158 + display_name: "Sceloporus jarrovii" +} +item { + name: "52628" + id: 1159 + display_name: "Araneus diadematus" +} +item { + name: "180007" + id: 1160 + display_name: "Otospermophilus beecheyi" +} +item { + name: "85408" + id: 1161 + display_name: "Erythemis collocata" +} +item { + name: "36262" + id: 1162 + display_name: "Sceloporus grammicus" +} +item { + name: "60839" + id: 1163 + display_name: "Spilosoma virginica" +} +item { + name: "16968" + id: 1164 + display_name: "Camptostoma imberbe" +} +item { + name: "4715" + id: 1165 + display_name: "Caracara plancus" +} +item { + name: "313246" + id: 1166 + display_name: "Olla v-nigrum" +} +item { + name: "126393" + id: 1167 + display_name: "Stomolophus meleagris" +} +item { + name: "126397" + id: 1168 + display_name: "Halysidota harrisii" +} +item { + name: "64221" + id: 1169 + display_name: "Bipalium kewense" +} +item { + name: "28102" + id: 1170 + display_name: "Virginia striatula" +} +item { + name: "150985" + id: 1171 + display_name: "Planorbella trivolvis" +} +item { + name: "36306" + id: 1172 + display_name: "Phrynosoma modestum" +} +item { + name: "36307" + id: 1173 + display_name: "Phrynosoma orbiculare" +} +item { + name: "199929" + id: 1174 + display_name: "Plagiometriona clavata" +} +item { + name: "3545" + id: 1175 + display_name: "Columbina passerina" +} +item { + name: "36315" + id: 1176 + display_name: "Phrynosoma hernandesi" +} +item { + name: "367556" + id: 1177 + display_name: "Eupsittula nana" +} +item { + name: "371963" + id: 1178 + display_name: "Lampropeltis multifasciata" +} +item { + name: "36339" + id: 1179 + display_name: "Holbrookia propinqua" +} +item { + name: "36094" + id: 1180 + display_name: "Uta stansburiana" +} +item { + name: "36343" + id: 1181 + display_name: "Holbrookia maculata" +} +item { + name: "52766" + id: 1182 + display_name: "Megaphasma denticrus" +} +item { + name: "18941" + id: 1183 + display_name: "Nestor notabilis" +} +item { + name: "3580" + id: 1184 + display_name: "Columbina talpacoti" +} +item { + name: "123690" + id: 1185 + display_name: "Caranx melampygus" +} +item { + name: "52482" + id: 1186 + display_name: "Episyrphus balteatus" +} +item { + name: "28762" + id: 1187 + display_name: "Rhinocheilus lecontei" +} +item { + name: "3607" + id: 1188 + display_name: "Geopelia striata" +} +item { + name: "52484" + id: 1189 + display_name: "Celastrina echo" +} +item { + name: "61293" + id: 1190 + display_name: "Thaumetopoea pityocampa" +} +item { + name: "19998" + id: 1191 + display_name: "Athene noctua" +} +item { + name: "44575" + id: 1192 + display_name: "Rattus rattus" +} +item { + name: "44576" + id: 1193 + display_name: "Rattus norvegicus" +} +item { + name: "133250" + id: 1194 + display_name: "Tettigonia viridissima" +} +item { + name: "52774" + id: 1195 + display_name: "Bombus fervidus" +} +item { + name: "49756" + id: 1196 + display_name: "Nephila clavipes" +} +item { + name: "52779" + id: 1197 + display_name: "Bombus bimaculatus" +} +item { + name: "52782" + id: 1198 + display_name: "Melissodes bimaculata" +} +item { + name: "126513" + id: 1199 + display_name: "Larinioides cornutus" +} +item { + name: "69170" + id: 1200 + display_name: "Hemigrapsus oregonensis" +} +item { + name: "1971" + id: 1201 + display_name: "Crotophaga ani" +} +item { + name: "12942" + id: 1202 + display_name: "Sialia sialis" +} +item { + name: "126532" + id: 1203 + display_name: "Toxomerus geminatus" +} +item { + name: "216649" + id: 1204 + display_name: "Chauliognathus pensylvanicus" +} +item { + name: "3734" + id: 1205 + display_name: "Platalea alba" +} +item { + name: "216651" + id: 1206 + display_name: "Chelinidea vittiger" +} +item { + name: "20044" + id: 1207 + display_name: "Bubo virginianus" +} +item { + name: "11855" + id: 1208 + display_name: "Petrochelidon fulva" +} +item { + name: "28246" + id: 1209 + display_name: "Arizona elegans" +} +item { + name: "224855" + id: 1210 + display_name: "Melipotis indomita" +} +item { + name: "11867" + id: 1211 + display_name: "Progne subis" +} +item { + name: "126562" + id: 1212 + display_name: "Setophaga coronata auduboni" +} +item { + name: "126568" + id: 1213 + display_name: "Manduca rustica" +} +item { + name: "11882" + id: 1214 + display_name: "Hirundo neoxena" +} +item { + name: "11901" + id: 1215 + display_name: "Hirundo rustica" +} +item { + name: "52865" + id: 1216 + display_name: "Tramea lacerata" +} +item { + name: "142978" + id: 1217 + display_name: "Simyra insularis" +} +item { + name: "123499" + id: 1218 + display_name: "Notophthalmus viridescens viridescens" +} +item { + name: "339592" + id: 1219 + display_name: "Calidris virgata" +} +item { + name: "339593" + id: 1220 + display_name: "Calidris pugnax" +} +item { + name: "44311" + id: 1221 + display_name: "Microtus pennsylvanicus" +} +item { + name: "142988" + id: 1222 + display_name: "Lerema accius" +} +item { + name: "142990" + id: 1223 + display_name: "Autographa precationis" +} +item { + name: "142995" + id: 1224 + display_name: "Hymenia perspectalis" +} +item { + name: "129423" + id: 1225 + display_name: "Zelus luridus" +} +item { + name: "3733" + id: 1226 + display_name: "Platalea regia" +} +item { + name: "470678" + id: 1227 + display_name: "Cerithideopsis californica" +} +item { + name: "146713" + id: 1228 + display_name: "Elaphria grata" +} +item { + name: "143002" + id: 1229 + display_name: "Orthonama obstipata" +} +item { + name: "11931" + id: 1230 + display_name: "Tachycineta thalassina" +} +item { + name: "143005" + id: 1231 + display_name: "Costaconvexa centrostrigaria" +} +item { + name: "3743" + id: 1232 + display_name: "Bostrychia hagedash" +} +item { + name: "143009" + id: 1233 + display_name: "Ectropis crepuscularia" +} +item { + name: "36514" + id: 1234 + display_name: "Anolis carolinensis" +} +item { + name: "143012" + id: 1235 + display_name: "Zanclognatha pedipilalis" +} +item { + name: "11941" + id: 1236 + display_name: "Riparia riparia" +} +item { + name: "52902" + id: 1237 + display_name: "Palthis asopialis" +} +item { + name: "3751" + id: 1238 + display_name: "Eudocimus albus" +} +item { + name: "52906" + id: 1239 + display_name: "Chytonix palliatricula" +} +item { + name: "3756" + id: 1240 + display_name: "Plegadis falcinellus" +} +item { + name: "3759" + id: 1241 + display_name: "Plegadis chihi" +} +item { + name: "143024" + id: 1242 + display_name: "Eusarca confusaria" +} +item { + name: "62067" + id: 1243 + display_name: "Orthetrum cancellatum" +} +item { + name: "28340" + id: 1244 + display_name: "Thamnophis sauritus" +} +item { + name: "28345" + id: 1245 + display_name: "Thamnophis cyrtopsis" +} +item { + name: "143034" + id: 1246 + display_name: "Hippodamia variegata" +} +item { + name: "28347" + id: 1247 + display_name: "Thamnophis cyrtopsis ocellatus" +} +item { + name: "52925" + id: 1248 + display_name: "Phyciodes tharos" +} +item { + name: "8010" + id: 1249 + display_name: "Corvus corax" +} +item { + name: "11970" + id: 1250 + display_name: "Stelgidopteryx serripennis" +} +item { + name: "28362" + id: 1251 + display_name: "Thamnophis sirtalis" +} +item { + name: "3788" + id: 1252 + display_name: "Sula dactylatra" +} +item { + name: "44749" + id: 1253 + display_name: "Neotoma fuscipes" +} +item { + name: "52943" + id: 1254 + display_name: "Trichodezia albovittata" +} +item { + name: "3793" + id: 1255 + display_name: "Sula sula" +} +item { + name: "101667" + id: 1256 + display_name: "Gomphus exilis" +} +item { + name: "3797" + id: 1257 + display_name: "Sula leucogaster" +} +item { + name: "118486" + id: 1258 + display_name: "Macaria aemulataria" +} +item { + name: "3801" + id: 1259 + display_name: "Morus serrator" +} +item { + name: "28378" + id: 1260 + display_name: "Thamnophis radix" +} +item { + name: "118492" + id: 1261 + display_name: "Helicoverpa zea" +} +item { + name: "148793" + id: 1262 + display_name: "Asterocampa leilia" +} +item { + name: "28384" + id: 1263 + display_name: "Thamnophis proximus rubrilineatus" +} +item { + name: "257761" + id: 1264 + display_name: "Phocides polybius" +} +item { + name: "28387" + id: 1265 + display_name: "Thamnophis proximus orarius" +} +item { + name: "28390" + id: 1266 + display_name: "Thamnophis marcianus" +} +item { + name: "118503" + id: 1267 + display_name: "Darapsa myron" +} +item { + name: "3817" + id: 1268 + display_name: "Eudyptula minor" +} +item { + name: "36135" + id: 1269 + display_name: "Uma scoparia" +} +item { + name: "28396" + id: 1270 + display_name: "Thamnophis hammondii" +} +item { + name: "28400" + id: 1271 + display_name: "Thamnophis elegans elegans" +} +item { + name: "118513" + id: 1272 + display_name: "Hypena scabra" +} +item { + name: "28403" + id: 1273 + display_name: "Thamnophis elegans vagrans" +} +item { + name: "201342" + id: 1274 + display_name: "Chalcoela iphitalis" +} +item { + name: "3831" + id: 1275 + display_name: "Megadyptes antipodes" +} +item { + name: "126712" + id: 1276 + display_name: "Corydalus cornutus" +} +item { + name: "30676" + id: 1277 + display_name: "Agkistrodon piscivorus leucostoma" +} +item { + name: "3834" + id: 1278 + display_name: "Scopus umbretta" +} +item { + name: "213631" + id: 1279 + display_name: "Anicla infecta" +} +item { + name: "143105" + id: 1280 + display_name: "Pleuroprucha insulsaria" +} +item { + name: "28418" + id: 1281 + display_name: "Thamnophis atratus" +} +item { + name: "118531" + id: 1282 + display_name: "Parallelia bistriaris" +} +item { + name: "145363" + id: 1283 + display_name: "Troglodytes troglodytes" +} +item { + name: "3845" + id: 1284 + display_name: "Calidris canutus" +} +item { + name: "12038" + id: 1285 + display_name: "Lanius collurio" +} +item { + name: "143114" + id: 1286 + display_name: "Phragmatobia fuliginosa" +} +item { + name: "3851" + id: 1287 + display_name: "Calidris bairdii" +} +item { + name: "324226" + id: 1288 + display_name: "Meleagris gallopavo intermedia" +} +item { + name: "143118" + id: 1289 + display_name: "Pseudeustrotia carneola" +} +item { + name: "3855" + id: 1290 + display_name: "Calidris mauri" +} +item { + name: "3856" + id: 1291 + display_name: "Calidris maritima" +} +item { + name: "3857" + id: 1292 + display_name: "Calidris alpina" +} +item { + name: "143124" + id: 1293 + display_name: "Parapediasia teterrella" +} +item { + name: "143125" + id: 1294 + display_name: "Hypena madefactalis" +} +item { + name: "3863" + id: 1295 + display_name: "Calidris ferruginea" +} +item { + name: "118552" + id: 1296 + display_name: "Felis catus" +} +item { + name: "3865" + id: 1297 + display_name: "Calidris melanotos" +} +item { + name: "3869" + id: 1298 + display_name: "Limnodromus griseus" +} +item { + name: "118558" + id: 1299 + display_name: "Manduca quinquemaculata" +} +item { + name: "118559" + id: 1300 + display_name: "Tetraopes tetrophthalmus" +} +item { + name: "12065" + id: 1301 + display_name: "Malurus cyaneus" +} +item { + name: "3878" + id: 1302 + display_name: "Tringa nebularia" +} +item { + name: "101681" + id: 1303 + display_name: "Gomphus militaris" +} +item { + name: "413483" + id: 1304 + display_name: "Todiramphus sanctus vagans" +} +item { + name: "3885" + id: 1305 + display_name: "Tringa ochropus" +} +item { + name: "3888" + id: 1306 + display_name: "Tringa glareola" +} +item { + name: "126770" + id: 1307 + display_name: "Vulpes vulpes fulvus" +} +item { + name: "3892" + id: 1308 + display_name: "Tringa melanoleuca" +} +item { + name: "3893" + id: 1309 + display_name: "Tringa flavipes" +} +item { + name: "126775" + id: 1310 + display_name: "Cervus elaphus nelsoni" +} +item { + name: "3896" + id: 1311 + display_name: "Numenius arquata" +} +item { + name: "126777" + id: 1312 + display_name: "Peucetia viridans" +} +item { + name: "3901" + id: 1313 + display_name: "Numenius phaeopus" +} +item { + name: "32058" + id: 1314 + display_name: "Elgaria multicarinata webbii" +} +item { + name: "413506" + id: 1315 + display_name: "Phalacrocorax carbo novaehollandiae" +} +item { + name: "413508" + id: 1316 + display_name: "Petroica macrocephala macrocephala" +} +item { + name: "413512" + id: 1317 + display_name: "Petroica australis longipes" +} +item { + name: "61258" + id: 1318 + display_name: "Junonia evarete" +} +item { + name: "28493" + id: 1319 + display_name: "Tantilla nigriceps" +} +item { + name: "413522" + id: 1320 + display_name: "Prosthemadera novaeseelandiae novaeseelandiae" +} +item { + name: "58506" + id: 1321 + display_name: "Polites themistocles" +} +item { + name: "28505" + id: 1322 + display_name: "Tantilla gracilis" +} +item { + name: "20315" + id: 1323 + display_name: "Asio flammeus" +} +item { + name: "143196" + id: 1324 + display_name: "Schinia arcigera" +} +item { + name: "413533" + id: 1325 + display_name: "Rhipidura fuliginosa fuliginosa" +} +item { + name: "3936" + id: 1326 + display_name: "Scolopax minor" +} +item { + name: "3938" + id: 1327 + display_name: "Arenaria interpres" +} +item { + name: "3941" + id: 1328 + display_name: "Arenaria melanocephala" +} +item { + name: "413543" + id: 1329 + display_name: "Rhipidura fuliginosa placabilis" +} +item { + name: "3947" + id: 1330 + display_name: "Limosa limosa" +} +item { + name: "3950" + id: 1331 + display_name: "Limosa haemastica" +} +item { + name: "126269" + id: 1332 + display_name: "Austrolestes colensonis" +} +item { + name: "3954" + id: 1333 + display_name: "Limosa fedoa" +} +item { + name: "199998" + id: 1334 + display_name: "Pedicia albivitta" +} +item { + name: "3959" + id: 1335 + display_name: "Phalaropus lobatus" +} +item { + name: "3962" + id: 1336 + display_name: "Bartramia longicauda" +} +item { + name: "199999" + id: 1337 + display_name: "Callopistria mollissima" +} +item { + name: "104426" + id: 1338 + display_name: "Lestes disjunctus" +} +item { + name: "126848" + id: 1339 + display_name: "Delphinia picta" +} +item { + name: "3951" + id: 1340 + display_name: "Limosa lapponica" +} +item { + name: "20356" + id: 1341 + display_name: "Aegolius acadicus" +} +item { + name: "121792" + id: 1342 + display_name: "Polistes carolina" +} +item { + name: "3978" + id: 1343 + display_name: "Actitis hypoleucos" +} +item { + name: "53911" + id: 1344 + display_name: "Cyprinus carpio" +} +item { + name: "135055" + id: 1345 + display_name: "Bufotes balearicus" +} +item { + name: "19121" + id: 1346 + display_name: "Trichoglossus haematodus" +} +item { + name: "28562" + id: 1347 + display_name: "Storeria dekayi" +} +item { + name: "28563" + id: 1348 + display_name: "Storeria dekayi texana" +} +item { + name: "20372" + id: 1349 + display_name: "Surnia ulula" +} +item { + name: "135064" + id: 1350 + display_name: "Bufotes viridis" +} +item { + name: "28570" + id: 1351 + display_name: "Storeria dekayi dekayi" +} +item { + name: "61341" + id: 1352 + display_name: "Narceus americanus" +} +item { + name: "7493" + id: 1353 + display_name: "Polioptila caerulea" +} +item { + name: "29339" + id: 1354 + display_name: "Natrix natrix" +} +item { + name: "9135" + id: 1355 + display_name: "Spizella passerina" +} +item { + name: "126889" + id: 1356 + display_name: "Toxomerus marginatus" +} +item { + name: "143274" + id: 1357 + display_name: "Gluphisia septentrionis" +} +item { + name: "343021" + id: 1358 + display_name: "Anguis fragilis" +} +item { + name: "14591" + id: 1359 + display_name: "Pycnonotus jocosus" +} +item { + name: "10227" + id: 1360 + display_name: "Passerina cyanea" +} +item { + name: "10228" + id: 1361 + display_name: "Passerina versicolor" +} +item { + name: "61371" + id: 1362 + display_name: "Panulirus interruptus" +} +item { + name: "143294" + id: 1363 + display_name: "Colias croceus" +} +item { + name: "135104" + id: 1364 + display_name: "Ichthyosaura alpestris" +} +item { + name: "83958" + id: 1365 + display_name: "Phryganidia californica" +} +item { + name: "143302" + id: 1366 + display_name: "Megapallifera mutabilis" +} +item { + name: "12231" + id: 1367 + display_name: "Manorina melanocephala" +} +item { + name: "200661" + id: 1368 + display_name: "Coluber constrictor mormon" +} +item { + name: "3681" + id: 1369 + display_name: "Ocyphaps lophotes" +} +item { + name: "4773" + id: 1370 + display_name: "Jabiru mycteria" +} +item { + name: "135140" + id: 1371 + display_name: "Taricha sierrae" +} +item { + name: "28649" + id: 1372 + display_name: "Sonora semiannulata" +} +item { + name: "53226" + id: 1373 + display_name: "Boisea rubrolineata" +} +item { + name: "53227" + id: 1374 + display_name: "Boisea trivittata" +} +item { + name: "14593" + id: 1375 + display_name: "Pycnonotus cafer" +} +item { + name: "61428" + id: 1376 + display_name: "Arion subfuscus" +} +item { + name: "333822" + id: 1377 + display_name: "Anser cygnoides domesticus" +} +item { + name: "41641" + id: 1378 + display_name: "Ursus arctos" +} +item { + name: "56602" + id: 1379 + display_name: "Plebejus lupini" +} +item { + name: "55295" + id: 1380 + display_name: "Grapsus grapsus" +} +item { + name: "36181" + id: 1381 + display_name: "Sceloporus cyanogenys" +} +item { + name: "41708" + id: 1382 + display_name: "Phoca vitulina" +} +item { + name: "118788" + id: 1383 + display_name: "Desmia funeralis" +} +item { + name: "61445" + id: 1384 + display_name: "Acanthocephala terminalis" +} +item { + name: "30721" + id: 1385 + display_name: "Crotalus triseriatus" +} +item { + name: "180010" + id: 1386 + display_name: "Callospermophilus lateralis" +} +item { + name: "53875" + id: 1387 + display_name: "Ocypode quadrata" +} +item { + name: "18358" + id: 1388 + display_name: "Picus viridis" +} +item { + name: "143390" + id: 1389 + display_name: "Oxidus gracilis" +} +item { + name: "55785" + id: 1390 + display_name: "Ochlodes agricola" +} +item { + name: "4141" + id: 1391 + display_name: "Phoebastria nigripes" +} +item { + name: "20526" + id: 1392 + display_name: "Struthio camelus" +} +item { + name: "32093" + id: 1393 + display_name: "Boa constrictor" +} +item { + name: "4144" + id: 1394 + display_name: "Phoebastria immutabilis" +} +item { + name: "74442" + id: 1395 + display_name: "Hydrochoerus hydrochaeris" +} +item { + name: "61492" + id: 1396 + display_name: "Chrysopilus thoracicus" +} +item { + name: "61495" + id: 1397 + display_name: "Erythemis simplicicollis" +} +item { + name: "389177" + id: 1398 + display_name: "Eriophora pustulosa" +} +item { + name: "61503" + id: 1399 + display_name: "Ascalapha odorata" +} +item { + name: "118855" + id: 1400 + display_name: "Calosoma scrutator" +} +item { + name: "61513" + id: 1401 + display_name: "Adelges tsugae" +} +item { + name: "28749" + id: 1402 + display_name: "Salvadora grahamiae" +} +item { + name: "143440" + id: 1403 + display_name: "Ceratomia catalpae" +} +item { + name: "61523" + id: 1404 + display_name: "Helix pomatia" +} +item { + name: "4180" + id: 1405 + display_name: "Fulmarus glacialis" +} +item { + name: "143445" + id: 1406 + display_name: "Pachysphinx modesta" +} +item { + name: "233560" + id: 1407 + display_name: "Vespula squamosa" +} +item { + name: "126308" + id: 1408 + display_name: "Marpesia chiron" +} +item { + name: "61536" + id: 1409 + display_name: "Calopteryx virgo" +} +item { + name: "685" + id: 1410 + display_name: "Francolinus pondicerianus" +} +item { + name: "60774" + id: 1411 + display_name: "Psychomorpha epimenis" +} +item { + name: "135271" + id: 1412 + display_name: "Amphibolips confluenta" +} +item { + name: "69736" + id: 1413 + display_name: "Schistocerca americana" +} +item { + name: "69737" + id: 1414 + display_name: "Xylophanes tersa" +} +item { + name: "6141" + id: 1415 + display_name: "Cynanthus latirostris" +} +item { + name: "4205" + id: 1416 + display_name: "Podiceps nigricollis" +} +item { + name: "69743" + id: 1417 + display_name: "Wallengrenia otho" +} +item { + name: "4208" + id: 1418 + display_name: "Podiceps cristatus" +} +item { + name: "4209" + id: 1419 + display_name: "Podiceps auritus" +} +item { + name: "118901" + id: 1420 + display_name: "Hyles gallii" +} +item { + name: "17871" + id: 1421 + display_name: "Dendrocopos major" +} +item { + name: "143484" + id: 1422 + display_name: "Blepharomastix ranalis" +} +item { + name: "4224" + id: 1423 + display_name: "Podiceps grisegena" +} +item { + name: "200834" + id: 1424 + display_name: "Sphenodon punctatus" +} +item { + name: "179995" + id: 1425 + display_name: "Urocitellus beldingi" +} +item { + name: "322024" + id: 1426 + display_name: "Apatura ilia" +} +item { + name: "44396" + id: 1427 + display_name: "Peromyscus maniculatus" +} +item { + name: "4237" + id: 1428 + display_name: "Tachybaptus ruficollis" +} +item { + name: "118930" + id: 1429 + display_name: "Spodoptera ornithogalli" +} +item { + name: "118936" + id: 1430 + display_name: "Euplagia quadripunctaria" +} +item { + name: "4804" + id: 1431 + display_name: "Charadrius montanus" +} +item { + name: "127133" + id: 1432 + display_name: "Hyphantria cunea" +} +item { + name: "143518" + id: 1433 + display_name: "Prochoerodes lineola" +} +item { + name: "52592" + id: 1434 + display_name: "Pararge aegeria" +} +item { + name: "36149" + id: 1435 + display_name: "Sceloporus torquatus" +} +item { + name: "118951" + id: 1436 + display_name: "Pterophylla camellifolia" +} +item { + name: "4265" + id: 1437 + display_name: "Phalacrocorax auritus" +} +item { + name: "4270" + id: 1438 + display_name: "Phalacrocorax carbo" +} +item { + name: "446640" + id: 1439 + display_name: "Neomonachus schauinslandi" +} +item { + name: "118961" + id: 1440 + display_name: "Conocephalus brevipennis" +} +item { + name: "28850" + id: 1441 + display_name: "Regina septemvittata" +} +item { + name: "4277" + id: 1442 + display_name: "Phalacrocorax penicillatus" +} +item { + name: "4234" + id: 1443 + display_name: "Aechmophorus clarkii" +} +item { + name: "118967" + id: 1444 + display_name: "Psyllobora vigintimaculata" +} +item { + name: "118968" + id: 1445 + display_name: "Allograpta obliqua" +} +item { + name: "118970" + id: 1446 + display_name: "Bombus impatiens" +} +item { + name: "123594" + id: 1447 + display_name: "Anaxyrus americanus americanus" +} +item { + name: "69838" + id: 1448 + display_name: "Cyanea capillata" +} +item { + name: "69844" + id: 1449 + display_name: "Anthocharis midea" +} +item { + name: "48505" + id: 1450 + display_name: "Junonia coenia" +} +item { + name: "151769" + id: 1451 + display_name: "Diaphania hyalinata" +} +item { + name: "151770" + id: 1452 + display_name: "Peridea angulosa" +} +item { + name: "53467" + id: 1453 + display_name: "Leucauge venusta" +} +item { + name: "119013" + id: 1454 + display_name: "Ctenucha virginica" +} +item { + name: "4327" + id: 1455 + display_name: "Pelecanus onocrotalus" +} +item { + name: "143592" + id: 1456 + display_name: "Spragueia leo" +} +item { + name: "200938" + id: 1457 + display_name: "Diaethria anna" +} +item { + name: "4334" + id: 1458 + display_name: "Pelecanus erythrorhynchos" +} +item { + name: "151794" + id: 1459 + display_name: "Atta texana" +} +item { + name: "3454" + id: 1460 + display_name: "Zenaida macroura" +} +item { + name: "4872" + id: 1461 + display_name: "Vanellus miles" +} +item { + name: "4345" + id: 1462 + display_name: "Larus occidentalis" +} +item { + name: "143610" + id: 1463 + display_name: "Besma quercivoraria" +} +item { + name: "20733" + id: 1464 + display_name: "Trogon massena" +} +item { + name: "143615" + id: 1465 + display_name: "Udea rubigalis" +} +item { + name: "4352" + id: 1466 + display_name: "Larus thayeri" +} +item { + name: "4353" + id: 1467 + display_name: "Larus heermanni" +} +item { + name: "4354" + id: 1468 + display_name: "Larus livens" +} +item { + name: "4356" + id: 1469 + display_name: "Larus canus" +} +item { + name: "220826" + id: 1470 + display_name: "Habrosyne scripta" +} +item { + name: "4361" + id: 1471 + display_name: "Larus glaucoides" +} +item { + name: "4364" + id: 1472 + display_name: "Larus delawarensis" +} +item { + name: "102672" + id: 1473 + display_name: "Hetaerina titia" +} +item { + name: "20754" + id: 1474 + display_name: "Trogon collaris" +} +item { + name: "479512" + id: 1475 + display_name: "Acronicta fallax" +} +item { + name: "3460" + id: 1476 + display_name: "Zenaida asiatica" +} +item { + name: "119066" + id: 1477 + display_name: "Idia lubricalis" +} +item { + name: "119068" + id: 1478 + display_name: "Apodemia virgulti" +} +item { + name: "4381" + id: 1479 + display_name: "Larus fuscus" +} +item { + name: "4385" + id: 1480 + display_name: "Larus californicus" +} +item { + name: "69922" + id: 1481 + display_name: "Oncorhynchus nerka" +} +item { + name: "12580" + id: 1482 + display_name: "Prosthemadera novaeseelandiae" +} +item { + name: "69925" + id: 1483 + display_name: "Clinocardium nuttallii" +} +item { + name: "20781" + id: 1484 + display_name: "Trogon elegans" +} +item { + name: "4399" + id: 1485 + display_name: "Larus glaucescens" +} +item { + name: "94513" + id: 1486 + display_name: "Archilestes grandis" +} +item { + name: "119090" + id: 1487 + display_name: "Eremnophila aureonotata" +} +item { + name: "20787" + id: 1488 + display_name: "Trogon citreolus" +} +item { + name: "69940" + id: 1489 + display_name: "Hemiargus ceraunus" +} +item { + name: "61749" + id: 1490 + display_name: "Lucanus cervus" +} +item { + name: "4415" + id: 1491 + display_name: "Cepphus columba" +} +item { + name: "4832" + id: 1492 + display_name: "Himantopus leucocephalus" +} +item { + name: "4418" + id: 1493 + display_name: "Cepphus grylle" +} +item { + name: "12612" + id: 1494 + display_name: "Anthornis melanura" +} +item { + name: "125627" + id: 1495 + display_name: "Ellychnia corrusca" +} +item { + name: "201031" + id: 1496 + display_name: "Leptoptilos crumenifer" +} +item { + name: "201032" + id: 1497 + display_name: "Threskiornis moluccus" +} +item { + name: "60812" + id: 1498 + display_name: "Lucanus capreolus" +} +item { + name: "10295" + id: 1499 + display_name: "Thraupis episcopus" +} +item { + name: "209233" + id: 1500 + display_name: "Equus caballus" +} +item { + name: "119122" + id: 1501 + display_name: "Araneus trifolium" +} +item { + name: "201043" + id: 1502 + display_name: "Geranoaetus albicaudatus" +} +item { + name: "61781" + id: 1503 + display_name: "Ochlodes sylvanus" +} +item { + name: "49133" + id: 1504 + display_name: "Vanessa atalanta" +} +item { + name: "94556" + id: 1505 + display_name: "Argia lugens" +} +item { + name: "94557" + id: 1506 + display_name: "Argia moesta" +} +item { + name: "61524" + id: 1507 + display_name: "Forficula auricularia" +} +item { + name: "4449" + id: 1508 + display_name: "Sterna paradisaea" +} +item { + name: "4450" + id: 1509 + display_name: "Sterna hirundo" +} +item { + name: "348515" + id: 1510 + display_name: "Nyctemera annulata" +} +item { + name: "110625" + id: 1511 + display_name: "Progomphus obscurus" +} +item { + name: "94566" + id: 1512 + display_name: "Argia plana" +} +item { + name: "4457" + id: 1513 + display_name: "Sterna forsteri" +} +item { + name: "94571" + id: 1514 + display_name: "Argia sedula" +} +item { + name: "61804" + id: 1515 + display_name: "Olivella biplicata" +} +item { + name: "204532" + id: 1516 + display_name: "Lanius excubitor" +} +item { + name: "29038" + id: 1517 + display_name: "Pituophis deppei" +} +item { + name: "143728" + id: 1518 + display_name: "Choristoneura rosaceana" +} +item { + name: "94577" + id: 1519 + display_name: "Argia translata" +} +item { + name: "130451" + id: 1520 + display_name: "Dione juno" +} +item { + name: "29044" + id: 1521 + display_name: "Pituophis catenifer" +} +item { + name: "70005" + id: 1522 + display_name: "Ilyanassa obsoleta" +} +item { + name: "143734" + id: 1523 + display_name: "Eupithecia miserulata" +} +item { + name: "20856" + id: 1524 + display_name: "Pharomachrus mocinno" +} +item { + name: "29049" + id: 1525 + display_name: "Pituophis catenifer deserticola" +} +item { + name: "29052" + id: 1526 + display_name: "Pituophis catenifer affinis" +} +item { + name: "29053" + id: 1527 + display_name: "Pituophis catenifer annectens" +} +item { + name: "4478" + id: 1528 + display_name: "Sterna striata" +} +item { + name: "407459" + id: 1529 + display_name: "Dolomedes minor" +} +item { + name: "4489" + id: 1530 + display_name: "Stercorarius parasiticus" +} +item { + name: "4491" + id: 1531 + display_name: "Stercorarius pomarinus" +} +item { + name: "6969" + id: 1532 + display_name: "Anas gracilis" +} +item { + name: "4494" + id: 1533 + display_name: "Rissa tridactyla" +} +item { + name: "4496" + id: 1534 + display_name: "Rynchops niger" +} +item { + name: "4501" + id: 1535 + display_name: "Alca torda" +} +item { + name: "4504" + id: 1536 + display_name: "Fratercula arctica" +} +item { + name: "4509" + id: 1537 + display_name: "Fratercula cirrhata" +} +item { + name: "26693" + id: 1538 + display_name: "Scaphiopus hurterii" +} +item { + name: "94624" + id: 1539 + display_name: "Arigomphus submedianus" +} +item { + name: "94625" + id: 1540 + display_name: "Arigomphus villosipes" +} +item { + name: "120720" + id: 1541 + display_name: "Pseudacris sierra" +} +item { + name: "70057" + id: 1542 + display_name: "Agrilus planipennis" +} +item { + name: "127402" + id: 1543 + display_name: "Grammia virgo" +} +item { + name: "51271" + id: 1544 + display_name: "Trachemys scripta elegans" +} +item { + name: "12716" + id: 1545 + display_name: "Turdus merula" +} +item { + name: "12718" + id: 1546 + display_name: "Turdus plumbeus" +} +item { + name: "12720" + id: 1547 + display_name: "Turdus grayi" +} +item { + name: "63697" + id: 1548 + display_name: "Metacarcinus magister" +} +item { + name: "12727" + id: 1549 + display_name: "Turdus migratorius" +} +item { + name: "26698" + id: 1550 + display_name: "Spea multiplicata" +} +item { + name: "12735" + id: 1551 + display_name: "Turdus viscivorus" +} +item { + name: "26699" + id: 1552 + display_name: "Spea bombifrons" +} +item { + name: "127431" + id: 1553 + display_name: "Emmelina monodactyla" +} +item { + name: "4553" + id: 1554 + display_name: "Cerorhinca monocerata" +} +item { + name: "12748" + id: 1555 + display_name: "Turdus philomelos" +} +item { + name: "233933" + id: 1556 + display_name: "Zale horrida" +} +item { + name: "1468" + id: 1557 + display_name: "Galbula ruficauda" +} +item { + name: "111055" + id: 1558 + display_name: "Pseudoleon superbus" +} +item { + name: "61908" + id: 1559 + display_name: "Orgyia vetusta" +} +item { + name: "43086" + id: 1560 + display_name: "Procavia capensis" +} +item { + name: "143830" + id: 1561 + display_name: "Eumorpha vitis" +} +item { + name: "67663" + id: 1562 + display_name: "Leptysma marginicollis" +} +item { + name: "127457" + id: 1563 + display_name: "Idia americalis" +} +item { + name: "4578" + id: 1564 + display_name: "Jacana spinosa" +} +item { + name: "127460" + id: 1565 + display_name: "Idia aemula" +} +item { + name: "201192" + id: 1566 + display_name: "Saxicola rubicola" +} +item { + name: "20969" + id: 1567 + display_name: "Upupa epops" +} +item { + name: "94699" + id: 1568 + display_name: "Aspidoscelis marmorata" +} +item { + name: "10322" + id: 1569 + display_name: "Euphagus carolinus" +} +item { + name: "53743" + id: 1570 + display_name: "Uca pugilator" +} +item { + name: "61256" + id: 1571 + display_name: "Leptoglossus phyllopus" +} +item { + name: "29438" + id: 1572 + display_name: "Coluber flagellum piceus" +} +item { + name: "53750" + id: 1573 + display_name: "Lottia gigantea" +} +item { + name: "143865" + id: 1574 + display_name: "Odocoileus hemionus hemionus" +} +item { + name: "143867" + id: 1575 + display_name: "Protoboarmia porcelaria" +} +item { + name: "209405" + id: 1576 + display_name: "Cenopis reticulatana" +} +item { + name: "49920" + id: 1577 + display_name: "Nymphalis californica" +} +item { + name: "53762" + id: 1578 + display_name: "Scolopendra polymorpha" +} +item { + name: "127492" + id: 1579 + display_name: "Megalographa biloba" +} +item { + name: "62470" + id: 1580 + display_name: "Limax maximus" +} +item { + name: "4621" + id: 1581 + display_name: "Gavia pacifica" +} +item { + name: "14884" + id: 1582 + display_name: "Mimus gilvus" +} +item { + name: "29200" + id: 1583 + display_name: "Opheodrys aestivus" +} +item { + name: "201233" + id: 1584 + display_name: "Passer italiae" +} +item { + name: "4626" + id: 1585 + display_name: "Gavia immer" +} +item { + name: "4627" + id: 1586 + display_name: "Gavia stellata" +} +item { + name: "12822" + id: 1587 + display_name: "Oenanthe oenanthe" +} +item { + name: "4631" + id: 1588 + display_name: "Fregata magnificens" +} +item { + name: "4636" + id: 1589 + display_name: "Fregata minor" +} +item { + name: "70174" + id: 1590 + display_name: "Hypolimnas bolina" +} +item { + name: "4643" + id: 1591 + display_name: "Falco subbuteo" +} +item { + name: "4644" + id: 1592 + display_name: "Falco mexicanus" +} +item { + name: "4645" + id: 1593 + display_name: "Falco femoralis" +} +item { + name: "4647" + id: 1594 + display_name: "Falco peregrinus" +} +item { + name: "119340" + id: 1595 + display_name: "Amphipyra pyramidoides" +} +item { + name: "61997" + id: 1596 + display_name: "Steatoda grossa" +} +item { + name: "70191" + id: 1597 + display_name: "Ischnura ramburii" +} +item { + name: "53809" + id: 1598 + display_name: "Phidippus audax" +} +item { + name: "143213" + id: 1599 + display_name: "Frontinella communis" +} +item { + name: "4664" + id: 1600 + display_name: "Falco rufigularis" +} +item { + name: "4665" + id: 1601 + display_name: "Falco sparverius" +} +item { + name: "19893" + id: 1602 + display_name: "Strix varia" +} +item { + name: "4672" + id: 1603 + display_name: "Falco columbarius" +} +item { + name: "201281" + id: 1604 + display_name: "Phyllodesma americana" +} +item { + name: "201282" + id: 1605 + display_name: "Gallinula chloropus" +} +item { + name: "152131" + id: 1606 + display_name: "Bagrada hilaris" +} +item { + name: "145276" + id: 1607 + display_name: "Cardellina pusilla" +} +item { + name: "12878" + id: 1608 + display_name: "Catharus ustulatus" +} +item { + name: "4690" + id: 1609 + display_name: "Falco novaeseelandiae" +} +item { + name: "53843" + id: 1610 + display_name: "Brephidium exilis" +} +item { + name: "36281" + id: 1611 + display_name: "Sceloporus clarkii" +} +item { + name: "12890" + id: 1612 + display_name: "Catharus guttatus" +} +item { + name: "62045" + id: 1613 + display_name: "Lygaeus kalmii" +} +item { + name: "47075" + id: 1614 + display_name: "Dasypus novemcinctus" +} +item { + name: "12901" + id: 1615 + display_name: "Catharus fuscescens" +} +item { + name: "4714" + id: 1616 + display_name: "Caracara cheriway" +} +item { + name: "53867" + id: 1617 + display_name: "Erythemis plebeja" +} +item { + name: "62060" + id: 1618 + display_name: "Palomena prasina" +} +item { + name: "53869" + id: 1619 + display_name: "Ocypus olens" +} +item { + name: "4719" + id: 1620 + display_name: "Herpetotheres cachinnans" +} +item { + name: "116840" + id: 1621 + display_name: "Calcarius lapponicus" +} +item { + name: "4726" + id: 1622 + display_name: "Milvago chimachima" +} +item { + name: "29304" + id: 1623 + display_name: "Nerodia taxispilota" +} +item { + name: "29305" + id: 1624 + display_name: "Nerodia sipedon" +} +item { + name: "29306" + id: 1625 + display_name: "Nerodia sipedon sipedon" +} +item { + name: "142783" + id: 1626 + display_name: "Myodocha serripes" +} +item { + name: "4733" + id: 1627 + display_name: "Ciconia ciconia" +} +item { + name: "29310" + id: 1628 + display_name: "Nerodia rhombifer" +} +item { + name: "201343" + id: 1629 + display_name: "Lithacodes fasciola" +} +item { + name: "21121" + id: 1630 + display_name: "Dendrobates auratus" +} +item { + name: "127618" + id: 1631 + display_name: "Epirrhoe alternata" +} +item { + name: "43115" + id: 1632 + display_name: "Sylvilagus audubonii" +} +item { + name: "29317" + id: 1633 + display_name: "Nerodia fasciata" +} +item { + name: "4742" + id: 1634 + display_name: "Mycteria americana" +} +item { + name: "53895" + id: 1635 + display_name: "Stenopelmatus fuscus" +} +item { + name: "4744" + id: 1636 + display_name: "Mycteria ibis" +} +item { + name: "12937" + id: 1637 + display_name: "Sialia mexicana" +} +item { + name: "29322" + id: 1638 + display_name: "Nerodia fasciata confluens" +} +item { + name: "29324" + id: 1639 + display_name: "Nerodia clarkii clarkii" +} +item { + name: "29327" + id: 1640 + display_name: "Nerodia cyclopion" +} +item { + name: "29328" + id: 1641 + display_name: "Nerodia erythrogaster" +} +item { + name: "53905" + id: 1642 + display_name: "Mantis religiosa" +} +item { + name: "4754" + id: 1643 + display_name: "Ephippiorhynchus senegalensis" +} +item { + name: "127635" + id: 1644 + display_name: "Plecia nearctica" +} +item { + name: "4756" + id: 1645 + display_name: "Cathartes aura" +} +item { + name: "29334" + id: 1646 + display_name: "Nerodia erythrogaster flavigaster" +} +item { + name: "12951" + id: 1647 + display_name: "Myadestes townsendi" +} +item { + name: "4761" + id: 1648 + display_name: "Cathartes burrovianus" +} +item { + name: "4763" + id: 1649 + display_name: "Sarcoramphus papa" +} +item { + name: "4765" + id: 1650 + display_name: "Coragyps atratus" +} +item { + name: "19890" + id: 1651 + display_name: "Strix nebulosa" +} +item { + name: "26736" + id: 1652 + display_name: "Ambystoma opacum" +} +item { + name: "66331" + id: 1653 + display_name: "Pelophylax perezi" +} +item { + name: "4776" + id: 1654 + display_name: "Anastomus lamelligerus" +} +item { + name: "4892" + id: 1655 + display_name: "Pluvialis squatarola" +} +item { + name: "4778" + id: 1656 + display_name: "Gymnogyps californianus" +} +item { + name: "12971" + id: 1657 + display_name: "Muscicapa striata" +} +item { + name: "56776" + id: 1658 + display_name: "Glaucopsyche lygdamus" +} +item { + name: "127669" + id: 1659 + display_name: "Jadera haematoloma" +} +item { + name: "4793" + id: 1660 + display_name: "Charadrius vociferus" +} +item { + name: "209594" + id: 1661 + display_name: "Scantius aegyptius" +} +item { + name: "4795" + id: 1662 + display_name: "Charadrius wilsonia" +} +item { + name: "48586" + id: 1663 + display_name: "Cepaea nemoralis" +} +item { + name: "4798" + id: 1664 + display_name: "Charadrius melodus" +} +item { + name: "12992" + id: 1665 + display_name: "Phoenicurus phoenicurus" +} +item { + name: "45763" + id: 1666 + display_name: "Ondatra zibethicus" +} +item { + name: "119492" + id: 1667 + display_name: "Smerinthus cerisyi" +} +item { + name: "13000" + id: 1668 + display_name: "Phoenicurus ochruros" +} +item { + name: "4811" + id: 1669 + display_name: "Charadrius dubius" +} +item { + name: "64973" + id: 1670 + display_name: "Anaxyrus cognatus" +} +item { + name: "2168" + id: 1671 + display_name: "Eumomota superciliosa" +} +item { + name: "6980" + id: 1672 + display_name: "Anas querquedula" +} +item { + name: "64975" + id: 1673 + display_name: "Anaxyrus debilis" +} +item { + name: "43130" + id: 1674 + display_name: "Lepus californicus" +} +item { + name: "67707" + id: 1675 + display_name: "Argiope aurantia" +} +item { + name: "4836" + id: 1676 + display_name: "Himantopus mexicanus" +} +item { + name: "4838" + id: 1677 + display_name: "Haematopus bachmani" +} +item { + name: "43132" + id: 1678 + display_name: "Lepus americanus" +} +item { + name: "144106" + id: 1679 + display_name: "Pica pica" +} +item { + name: "4843" + id: 1680 + display_name: "Haematopus ostralegus" +} +item { + name: "67709" + id: 1681 + display_name: "Antrodiaetus riversi" +} +item { + name: "4848" + id: 1682 + display_name: "Haematopus unicolor" +} +item { + name: "4857" + id: 1683 + display_name: "Vanellus vanellus" +} +item { + name: "29435" + id: 1684 + display_name: "Coluber flagellum testaceus" +} +item { + name: "119550" + id: 1685 + display_name: "Feltia jaculifera" +} +item { + name: "4866" + id: 1686 + display_name: "Vanellus spinosus" +} +item { + name: "4870" + id: 1687 + display_name: "Vanellus armatus" +} +item { + name: "54024" + id: 1688 + display_name: "Satyrium californica" +} +item { + name: "13071" + id: 1689 + display_name: "Luscinia svecica" +} +item { + name: "3544" + id: 1690 + display_name: "Columbina inca" +} +item { + name: "4883" + id: 1691 + display_name: "Recurvirostra avosetta" +} +item { + name: "204701" + id: 1692 + display_name: "Melanchra adjuncta" +} +item { + name: "56083" + id: 1693 + display_name: "Armadillidium vulgare" +} +item { + name: "981" + id: 1694 + display_name: "Phasianus colchicus" +} +item { + name: "4893" + id: 1695 + display_name: "Pluvialis dominica" +} +item { + name: "103200" + id: 1696 + display_name: "Hypsiglena jani" +} +item { + name: "127777" + id: 1697 + display_name: "Vespula vulgaris" +} +item { + name: "7643" + id: 1698 + display_name: "Cinclus mexicanus" +} +item { + name: "13094" + id: 1699 + display_name: "Erithacus rubecula" +} +item { + name: "41777" + id: 1700 + display_name: "Lontra canadensis" +} +item { + name: "64988" + id: 1701 + display_name: "Anaxyrus terrestris" +} +item { + name: "18167" + id: 1702 + display_name: "Melanerpes aurifrons" +} +item { + name: "54064" + id: 1703 + display_name: "Polygonia comma" +} +item { + name: "209713" + id: 1704 + display_name: "Phigalia titea" +} +item { + name: "54068" + id: 1705 + display_name: "Boloria selene" +} +item { + name: "104585" + id: 1706 + display_name: "Libellula semifasciata" +} +item { + name: "119608" + id: 1707 + display_name: "Theba pisana" +} +item { + name: "4801" + id: 1708 + display_name: "Charadrius hiaticula" +} +item { + name: "104586" + id: 1709 + display_name: "Libellula vibrans" +} +item { + name: "4935" + id: 1710 + display_name: "Egretta gularis" +} +item { + name: "4937" + id: 1711 + display_name: "Egretta caerulea" +} +item { + name: "4938" + id: 1712 + display_name: "Egretta tricolor" +} +item { + name: "4940" + id: 1713 + display_name: "Egretta thula" +} +item { + name: "340813" + id: 1714 + display_name: "Hyalymenus tarsatus" +} +item { + name: "4943" + id: 1715 + display_name: "Egretta garzetta" +} +item { + name: "4947" + id: 1716 + display_name: "Egretta sacra" +} +item { + name: "13141" + id: 1717 + display_name: "Monticola solitarius" +} +item { + name: "4952" + id: 1718 + display_name: "Ardea cocoi" +} +item { + name: "4954" + id: 1719 + display_name: "Ardea cinerea" +} +item { + name: "67727" + id: 1720 + display_name: "Aeshna umbrosa" +} +item { + name: "4956" + id: 1721 + display_name: "Ardea herodias" +} +item { + name: "144223" + id: 1722 + display_name: "Chlosyne theona" +} +item { + name: "201568" + id: 1723 + display_name: "Diabrotica undecimpunctata undecimpunctata" +} +item { + name: "47383" + id: 1724 + display_name: "Latrodectus geometricus" +} +item { + name: "119664" + id: 1725 + display_name: "Cacyreus marshalli" +} +item { + name: "62321" + id: 1726 + display_name: "Rutpela maculata" +} +item { + name: "217970" + id: 1727 + display_name: "Cyclophora pendulinaria" +} +item { + name: "4981" + id: 1728 + display_name: "Nycticorax nycticorax" +} +item { + name: "12714" + id: 1729 + display_name: "Turdus rufopalliatus" +} +item { + name: "4994" + id: 1730 + display_name: "Ardeola ralloides" +} +item { + name: "4999" + id: 1731 + display_name: "Nyctanassa violacea" +} +item { + name: "37769" + id: 1732 + display_name: "Plestiodon skiltonianus" +} +item { + name: "213826" + id: 1733 + display_name: "Apamea amputatrix" +} +item { + name: "67736" + id: 1734 + display_name: "Rhionaeschna californica" +} +item { + name: "155380" + id: 1735 + display_name: "Andricus crystallinus" +} +item { + name: "144280" + id: 1736 + display_name: "Aramides cajaneus" +} +item { + name: "5017" + id: 1737 + display_name: "Bubulcus ibis" +} +item { + name: "5020" + id: 1738 + display_name: "Butorides virescens" +} +item { + name: "144285" + id: 1739 + display_name: "Porphyrio martinicus" +} +item { + name: "81729" + id: 1740 + display_name: "Feniseca tarquinius" +} +item { + name: "127905" + id: 1741 + display_name: "Bombus ternarius" +} +item { + name: "5034" + id: 1742 + display_name: "Botaurus lentiginosus" +} +item { + name: "29330" + id: 1743 + display_name: "Nerodia erythrogaster transversa" +} +item { + name: "5036" + id: 1744 + display_name: "Cochlearius cochlearius" +} +item { + name: "46001" + id: 1745 + display_name: "Sciurus vulgaris" +} +item { + name: "46005" + id: 1746 + display_name: "Sciurus variegatoides" +} +item { + name: "127928" + id: 1747 + display_name: "Autochton cellus" +} +item { + name: "340923" + id: 1748 + display_name: "Scolypopa australis" +} +item { + name: "46017" + id: 1749 + display_name: "Sciurus carolinensis" +} +item { + name: "46018" + id: 1750 + display_name: "Sciurus aberti" +} +item { + name: "447427" + id: 1751 + display_name: "Neverita lewisii" +} +item { + name: "46020" + id: 1752 + display_name: "Sciurus niger" +} +item { + name: "5061" + id: 1753 + display_name: "Anhinga novaehollandiae" +} +item { + name: "46023" + id: 1754 + display_name: "Sciurus griseus" +} +item { + name: "122375" + id: 1755 + display_name: "Carterocephalus palaemon" +} +item { + name: "5066" + id: 1756 + display_name: "Anhinga rufa" +} +item { + name: "145289" + id: 1757 + display_name: "Melozone fusca" +} +item { + name: "5074" + id: 1758 + display_name: "Aquila chrysaetos" +} +item { + name: "49998" + id: 1759 + display_name: "Thamnophis sirtalis infernalis" +} +item { + name: "13270" + id: 1760 + display_name: "Hylocichla mustelina" +} +item { + name: "62423" + id: 1761 + display_name: "Cimbex americana" +} +item { + name: "62424" + id: 1762 + display_name: "Sitochroa palealis" +} +item { + name: "111578" + id: 1763 + display_name: "Regina grahamii" +} +item { + name: "144207" + id: 1764 + display_name: "Aphelocoma wollweberi" +} +item { + name: "62429" + id: 1765 + display_name: "Pyronia tithonus" +} +item { + name: "47934" + id: 1766 + display_name: "Libellula luctuosa" +} +item { + name: "50000" + id: 1767 + display_name: "Clemmys guttata" +} +item { + name: "5097" + id: 1768 + display_name: "Accipiter striatus" +} +item { + name: "119789" + id: 1769 + display_name: "Cisseps fulvicollis" +} +item { + name: "5106" + id: 1770 + display_name: "Accipiter nisus" +} +item { + name: "5108" + id: 1771 + display_name: "Accipiter gentilis" +} +item { + name: "62456" + id: 1772 + display_name: "Rhagonycha fulva" +} +item { + name: "4948" + id: 1773 + display_name: "Egretta rufescens" +} +item { + name: "46082" + id: 1774 + display_name: "Marmota marmota" +} +item { + name: "6990" + id: 1775 + display_name: "Bucephala clangula" +} +item { + name: "4535" + id: 1776 + display_name: "Anous stolidus" +} +item { + name: "46087" + id: 1777 + display_name: "Marmota caligata" +} +item { + name: "72458" + id: 1778 + display_name: "Actitis macularius" +} +item { + name: "4951" + id: 1779 + display_name: "Ardea purpurea" +} +item { + name: "128012" + id: 1780 + display_name: "Eumorpha fasciatus" +} +item { + name: "472078" + id: 1781 + display_name: "Todiramphus chloris" +} +item { + name: "46095" + id: 1782 + display_name: "Marmota monax" +} +item { + name: "34" + id: 1783 + display_name: "Grus americana" +} +item { + name: "4835" + id: 1784 + display_name: "Himantopus himantopus" +} +item { + name: "122374" + id: 1785 + display_name: "Eurema mexicana" +} +item { + name: "19812" + id: 1786 + display_name: "Glaucidium gnoma" +} +item { + name: "73823" + id: 1787 + display_name: "Hierophis viridiflavus" +} +item { + name: "5168" + id: 1788 + display_name: "Circus approximans" +} +item { + name: "143110" + id: 1789 + display_name: "Hypagyrtis unipunctata" +} +item { + name: "65976" + id: 1790 + display_name: "Lithobates blairi" +} +item { + name: "5173" + id: 1791 + display_name: "Circus aeruginosus" +} +item { + name: "54327" + id: 1792 + display_name: "Vespa crabro" +} +item { + name: "4273" + id: 1793 + display_name: "Phalacrocorax sulcirostris" +} +item { + name: "5180" + id: 1794 + display_name: "Buteo albonotatus" +} +item { + name: "103485" + id: 1795 + display_name: "Ischnura denticollis" +} +item { + name: "62528" + id: 1796 + display_name: "Butorides striata" +} +item { + name: "62529" + id: 1797 + display_name: "Platalea ajaja" +} +item { + name: "5186" + id: 1798 + display_name: "Buteo brachyurus" +} +item { + name: "103494" + id: 1799 + display_name: "Ischnura hastata" +} +item { + name: "144455" + id: 1800 + display_name: "Ardea alba" +} +item { + name: "103497" + id: 1801 + display_name: "Ischnura perparva" +} +item { + name: "103498" + id: 1802 + display_name: "Ischnura posita" +} +item { + name: "5196" + id: 1803 + display_name: "Buteo swainsoni" +} +item { + name: "128079" + id: 1804 + display_name: "Grammia ornata" +} +item { + name: "29777" + id: 1805 + display_name: "Lampropeltis triangulum" +} +item { + name: "867" + id: 1806 + display_name: "Alectoris rufa" +} +item { + name: "5206" + id: 1807 + display_name: "Buteo lineatus" +} +item { + name: "29783" + id: 1808 + display_name: "Lampropeltis triangulum triangulum" +} +item { + name: "122383" + id: 1809 + display_name: "Plebejus melissa" +} +item { + name: "5212" + id: 1810 + display_name: "Buteo jamaicensis" +} +item { + name: "81495" + id: 1811 + display_name: "Libellula pulchella" +} +item { + name: "35003" + id: 1812 + display_name: "Heloderma suspectum" +} +item { + name: "46180" + id: 1813 + display_name: "Cynomys gunnisoni" +} +item { + name: "144485" + id: 1814 + display_name: "Charadrius nivosus" +} +item { + name: "144490" + id: 1815 + display_name: "Tringa incana" +} +item { + name: "144491" + id: 1816 + display_name: "Tringa semipalmata" +} +item { + name: "25185" + id: 1817 + display_name: "Hypopachus variolosus" +} +item { + name: "5231" + id: 1818 + display_name: "Terathopius ecaudatus" +} +item { + name: "144496" + id: 1819 + display_name: "Gallinago delicata" +} +item { + name: "5233" + id: 1820 + display_name: "Buteogallus anthracinus" +} +item { + name: "211035" + id: 1821 + display_name: "Speranza pustularia" +} +item { + name: "29813" + id: 1822 + display_name: "Lampropeltis getula" +} +item { + name: "144502" + id: 1823 + display_name: "Chroicocephalus philadelphia" +} +item { + name: "5242" + id: 1824 + display_name: "Circaetus gallicus" +} +item { + name: "144507" + id: 1825 + display_name: "Chroicocephalus novaehollandiae" +} +item { + name: "144510" + id: 1826 + display_name: "Chroicocephalus ridibundus" +} +item { + name: "52757" + id: 1827 + display_name: "Polistes fuscatus" +} +item { + name: "144514" + id: 1828 + display_name: "Leucophaeus atricilla" +} +item { + name: "144515" + id: 1829 + display_name: "Leucophaeus pipixcan" +} +item { + name: "46217" + id: 1830 + display_name: "Tamias striatus" +} +item { + name: "144525" + id: 1831 + display_name: "Onychoprion fuscatus" +} +item { + name: "46222" + id: 1832 + display_name: "Tamias minimus" +} +item { + name: "144530" + id: 1833 + display_name: "Sternula antillarum" +} +item { + name: "46230" + id: 1834 + display_name: "Tamias merriami" +} +item { + name: "144537" + id: 1835 + display_name: "Hydroprogne caspia" +} +item { + name: "144539" + id: 1836 + display_name: "Thalasseus maximus" +} +item { + name: "144540" + id: 1837 + display_name: "Thalasseus bergii" +} +item { + name: "5277" + id: 1838 + display_name: "Elanus leucurus" +} +item { + name: "324766" + id: 1839 + display_name: "Epicallima argenticinctella" +} +item { + name: "72486" + id: 1840 + display_name: "Alopochen aegyptiaca" +} +item { + name: "62229" + id: 1841 + display_name: "Ischnura cervula" +} +item { + name: "144550" + id: 1842 + display_name: "Streptopelia senegalensis" +} +item { + name: "46256" + id: 1843 + display_name: "Ammospermophilus harrisii" +} +item { + name: "94559" + id: 1844 + display_name: "Argia nahuana" +} +item { + name: "46259" + id: 1845 + display_name: "Tamiasciurus douglasii" +} +item { + name: "46260" + id: 1846 + display_name: "Tamiasciurus hudsonicus" +} +item { + name: "119989" + id: 1847 + display_name: "Stagmomantis carolina" +} +item { + name: "13494" + id: 1848 + display_name: "Gerygone igata" +} +item { + name: "5305" + id: 1849 + display_name: "Haliaeetus leucocephalus" +} +item { + name: "7596" + id: 1850 + display_name: "Cistothorus platensis" +} +item { + name: "5308" + id: 1851 + display_name: "Haliaeetus vocifer" +} +item { + name: "218301" + id: 1852 + display_name: "Diacme elealis" +} +item { + name: "95422" + id: 1853 + display_name: "Basiaeschna janata" +} +item { + name: "46272" + id: 1854 + display_name: "Glaucomys volans" +} +item { + name: "120010" + id: 1855 + display_name: "Polistes metricus" +} +item { + name: "144594" + id: 1856 + display_name: "Bubo scandiacus" +} +item { + name: "52771" + id: 1857 + display_name: "Gonepteryx rhamni" +} +item { + name: "144597" + id: 1858 + display_name: "Ciccaba virgata" +} +item { + name: "890" + id: 1859 + display_name: "Bonasa umbellus" +} +item { + name: "52773" + id: 1860 + display_name: "Poanes zabulon" +} +item { + name: "120033" + id: 1861 + display_name: "Lapara bombycoides" +} +item { + name: "5346" + id: 1862 + display_name: "Busarellus nigricollis" +} +item { + name: "5349" + id: 1863 + display_name: "Rostrhamus sociabilis" +} +item { + name: "36391" + id: 1864 + display_name: "Anolis equestris" +} +item { + name: "46316" + id: 1865 + display_name: "Trichechus manatus" +} +item { + name: "5267" + id: 1866 + display_name: "Milvus milvus" +} +item { + name: "128241" + id: 1867 + display_name: "Darapsa choerilus" +} +item { + name: "128242" + id: 1868 + display_name: "Palthis angulalis" +} +item { + name: "5366" + id: 1869 + display_name: "Gyps fulvus" +} +item { + name: "204512" + id: 1870 + display_name: "Ficedula hypoleuca" +} +item { + name: "54526" + id: 1871 + display_name: "Crassadoma gigantea" +} +item { + name: "144642" + id: 1872 + display_name: "Momotus coeruliceps" +} +item { + name: "120070" + id: 1873 + display_name: "Strongylocentrotus droebachiensis" +} +item { + name: "54538" + id: 1874 + display_name: "Syngnathus leptorhynchus" +} +item { + name: "81746" + id: 1875 + display_name: "Necrophila americana" +} +item { + name: "300301" + id: 1876 + display_name: "Pseudomyrmex gracilis" +} +item { + name: "202003" + id: 1877 + display_name: "Apiomerus spissipes" +} +item { + name: "41860" + id: 1878 + display_name: "Enhydra lutris" +} +item { + name: "4817" + id: 1879 + display_name: "Charadrius semipalmatus" +} +item { + name: "36145" + id: 1880 + display_name: "Sceloporus variabilis" +} +item { + name: "202012" + id: 1881 + display_name: "Steatoda capensis" +} +item { + name: "62749" + id: 1882 + display_name: "Iphiclides podalirius" +} +item { + name: "5406" + id: 1883 + display_name: "Haliastur indus" +} +item { + name: "62751" + id: 1884 + display_name: "Andricus kingi" +} +item { + name: "5363" + id: 1885 + display_name: "Gyps africanus" +} +item { + name: "5416" + id: 1886 + display_name: "Ictinia mississippiensis" +} +item { + name: "62766" + id: 1887 + display_name: "Issoria lathonia" +} +item { + name: "62768" + id: 1888 + display_name: "Scolia dubia" +} +item { + name: "126206" + id: 1889 + display_name: "Dissosteira carolina" +} +item { + name: "269875" + id: 1890 + display_name: "Mallodon dasystomus" +} +item { + name: "155030" + id: 1891 + display_name: "Limenitis reducta" +} +item { + name: "62345" + id: 1892 + display_name: "Duttaphrynus melanostictus" +} +item { + name: "52519" + id: 1893 + display_name: "Aeshna cyanea" +} +item { + name: "10001" + id: 1894 + display_name: "Dives dives" +} +item { + name: "460365" + id: 1895 + display_name: "Tegula funebralis" +} +item { + name: "13631" + id: 1896 + display_name: "Baeolophus atricristatus" +} +item { + name: "13632" + id: 1897 + display_name: "Baeolophus bicolor" +} +item { + name: "13633" + id: 1898 + display_name: "Baeolophus inornatus" +} +item { + name: "9100" + id: 1899 + display_name: "Melospiza melodia" +} +item { + name: "62796" + id: 1900 + display_name: "Crotaphytus bicinctores" +} +item { + name: "62797" + id: 1901 + display_name: "Gambelia wislizenii" +} +item { + name: "46009" + id: 1902 + display_name: "Sciurus aureogaster" +} +item { + name: "112867" + id: 1903 + display_name: "Sparisoma viride" +} +item { + name: "70997" + id: 1904 + display_name: "Pelecinus polyturator" +} +item { + name: "62806" + id: 1905 + display_name: "Mytilus californianus" +} +item { + name: "120156" + id: 1906 + display_name: "Musca domestica" +} +item { + name: "136548" + id: 1907 + display_name: "Euclea delphinii" +} +item { + name: "50065" + id: 1908 + display_name: "Danaus eresimus" +} +item { + name: "43239" + id: 1909 + display_name: "Tachyglossus aculeatus" +} +item { + name: "145303" + id: 1910 + display_name: "Spinus spinus" +} +item { + name: "120183" + id: 1911 + display_name: "Araneus marmoreus" +} +item { + name: "71032" + id: 1912 + display_name: "Crotalus scutulatus scutulatus" +} +item { + name: "71034" + id: 1913 + display_name: "Tenodera sinensis" +} +item { + name: "143121" + id: 1914 + display_name: "Ochropleura implecta" +} +item { + name: "13695" + id: 1915 + display_name: "Motacilla alba" +} +item { + name: "7458" + id: 1916 + display_name: "Certhia americana" +} +item { + name: "38293" + id: 1917 + display_name: "Lampropholis delicata" +} +item { + name: "144281" + id: 1918 + display_name: "Bucorvus leadbeateri" +} +item { + name: "120217" + id: 1919 + display_name: "Halysidota tessellaris" +} +item { + name: "226718" + id: 1920 + display_name: "Otiorhynchus sulcatus" +} +item { + name: "464287" + id: 1921 + display_name: "Anteaeolidiella oliviae" +} +item { + name: "226720" + id: 1922 + display_name: "Oxychilus draparnaudi" +} +item { + name: "13729" + id: 1923 + display_name: "Anthus pratensis" +} +item { + name: "13732" + id: 1924 + display_name: "Anthus rubescens" +} +item { + name: "11930" + id: 1925 + display_name: "Tachycineta albilinea" +} +item { + name: "71085" + id: 1926 + display_name: "Varanus niloticus" +} +item { + name: "144814" + id: 1927 + display_name: "Poecile carolinensis" +} +item { + name: "144815" + id: 1928 + display_name: "Poecile atricapillus" +} +item { + name: "144816" + id: 1929 + display_name: "Poecile gambeli" +} +item { + name: "144820" + id: 1930 + display_name: "Poecile rufescens" +} +item { + name: "144823" + id: 1931 + display_name: "Periparus ater" +} +item { + name: "10485" + id: 1932 + display_name: "Chlorophanes spiza" +} +item { + name: "40523" + id: 1933 + display_name: "Lasiurus cinereus" +} +item { + name: "47719" + id: 1934 + display_name: "Datana ministra" +} +item { + name: "13770" + id: 1935 + display_name: "Estrilda astrild" +} +item { + name: "144849" + id: 1936 + display_name: "Cyanistes caeruleus" +} +item { + name: "218587" + id: 1937 + display_name: "Discus rotundatus" +} +item { + name: "47105" + id: 1938 + display_name: "Tamandua mexicana" +} +item { + name: "18463" + id: 1939 + display_name: "Sphyrapicus varius" +} +item { + name: "11858" + id: 1940 + display_name: "Petrochelidon pyrrhonota" +} +item { + name: "144882" + id: 1941 + display_name: "Troglodytes pacificus" +} +item { + name: "144883" + id: 1942 + display_name: "Troglodytes hiemalis" +} +item { + name: "153076" + id: 1943 + display_name: "Nephelodes minians" +} +item { + name: "62978" + id: 1944 + display_name: "Chlosyne nycteis" +} +item { + name: "128517" + id: 1945 + display_name: "Catocala ilia" +} +item { + name: "153102" + id: 1946 + display_name: "Dysphania militaris" +} +item { + name: "59651" + id: 1947 + display_name: "Aquarius remigis" +} +item { + name: "13851" + id: 1948 + display_name: "Passer montanus" +} +item { + name: "13858" + id: 1949 + display_name: "Passer domesticus" +} +item { + name: "39742" + id: 1950 + display_name: "Kinosternon flavescens" +} +item { + name: "506118" + id: 1951 + display_name: "Aphelocoma californica" +} +item { + name: "5672" + id: 1952 + display_name: "Amazilia yucatanensis" +} +item { + name: "5676" + id: 1953 + display_name: "Amazilia tzacatl" +} +item { + name: "204503" + id: 1954 + display_name: "Dicrurus adsimilis" +} +item { + name: "52785" + id: 1955 + display_name: "Megachile sculpturalis" +} +item { + name: "126905" + id: 1956 + display_name: "Harrisina americana" +} +item { + name: "55773" + id: 1957 + display_name: "Promachus hinei" +} +item { + name: "84752" + id: 1958 + display_name: "Microcentrum rhombifolium" +} +item { + name: "5698" + id: 1959 + display_name: "Amazilia violiceps" +} +item { + name: "145539" + id: 1960 + display_name: "Ovis canadensis nelsoni" +} +item { + name: "104004" + id: 1961 + display_name: "Lampropeltis splendida" +} +item { + name: "13893" + id: 1962 + display_name: "Lonchura punctulata" +} +item { + name: "63048" + id: 1963 + display_name: "Nuttallina californica" +} +item { + name: "226901" + id: 1964 + display_name: "Panopoda rufimargo" +} +item { + name: "194134" + id: 1965 + display_name: "Anthanassa tulcis" +} +item { + name: "5049" + id: 1966 + display_name: "Tigrisoma mexicanum" +} +item { + name: "407130" + id: 1967 + display_name: "Porphyrio melanotus melanotus" +} +item { + name: "226910" + id: 1968 + display_name: "Panthea furcilla" +} +item { + name: "130661" + id: 1969 + display_name: "Catasticta nimbice" +} +item { + name: "120215" + id: 1970 + display_name: "Bombus griseocollis" +} +item { + name: "144220" + id: 1971 + display_name: "Melanitta americana" +} +item { + name: "9148" + id: 1972 + display_name: "Spizella pallida" +} +item { + name: "320610" + id: 1973 + display_name: "Sceloporus magister" +} +item { + name: "54900" + id: 1974 + display_name: "Papilio polyxenes asterius" +} +item { + name: "36080" + id: 1975 + display_name: "Callisaurus draconoides" +} +item { + name: "5758" + id: 1976 + display_name: "Amazilia rutila" +} +item { + name: "3465" + id: 1977 + display_name: "Zenaida aurita" +} +item { + name: "116461" + id: 1978 + display_name: "Anolis sagrei" +} +item { + name: "61295" + id: 1979 + display_name: "Aporia crataegi" +} +item { + name: "131673" + id: 1980 + display_name: "Tetracis cachexiata" +} +item { + name: "63113" + id: 1981 + display_name: "Blarina brevicauda" +} +item { + name: "26904" + id: 1982 + display_name: "Coronella austriaca" +} +item { + name: "94575" + id: 1983 + display_name: "Argia tibialis" +} +item { + name: "237166" + id: 1984 + display_name: "Lycaena phlaeas hypophlaeas" +} +item { + name: "129305" + id: 1985 + display_name: "Melanoplus bivittatus" +} +item { + name: "63128" + id: 1986 + display_name: "Speyeria atlantis" +} +item { + name: "113514" + id: 1987 + display_name: "Sympetrum internum" +} +item { + name: "48757" + id: 1988 + display_name: "Echinothrix calamaris" +} +item { + name: "128670" + id: 1989 + display_name: "Bombus vagans" +} +item { + name: "13988" + id: 1990 + display_name: "Prunella modularis" +} +item { + name: "54951" + id: 1991 + display_name: "Anartia fatima" +} +item { + name: "54952" + id: 1992 + display_name: "Cardisoma guanhumi" +} +item { + name: "325295" + id: 1993 + display_name: "Cydalima perspectalis" +} +item { + name: "63160" + id: 1994 + display_name: "Celithemis elisa" +} +item { + name: "210615" + id: 1995 + display_name: "Pyrausta volupialis" +} +item { + name: "472766" + id: 1996 + display_name: "Falco tinnunculus" +} +item { + name: "29927" + id: 1997 + display_name: "Heterodon nasicus" +} +item { + name: "145088" + id: 1998 + display_name: "Ixoreus naevius" +} +item { + name: "6432" + id: 1999 + display_name: "Archilochus colubris" +} +item { + name: "5827" + id: 2000 + display_name: "Lampornis clemenciae" +} +item { + name: "15990" + id: 2001 + display_name: "Myiarchus tuberculifer" +} +item { + name: "128712" + id: 2002 + display_name: "Coccinella californica" +} +item { + name: "67559" + id: 2003 + display_name: "Adelpha eulalia" +} +item { + name: "128719" + id: 2004 + display_name: "Echinometra mathaei" +} +item { + name: "10247" + id: 2005 + display_name: "Setophaga ruticilla" +} +item { + name: "202451" + id: 2006 + display_name: "Copaeodes minima" +} +item { + name: "95958" + id: 2007 + display_name: "Boyeria vinosa" +} +item { + name: "16016" + id: 2008 + display_name: "Myiarchus tyrannulus" +} +item { + name: "36202" + id: 2009 + display_name: "Sceloporus olivaceus" +} +item { + name: "95982" + id: 2010 + display_name: "Brachymesia furcata" +} +item { + name: "126589" + id: 2011 + display_name: "Calycopis isobeon" +} +item { + name: "120578" + id: 2012 + display_name: "Micrathena sagittata" +} +item { + name: "194690" + id: 2013 + display_name: "Pogonomyrmex barbatus" +} +item { + name: "120583" + id: 2014 + display_name: "Parasteatoda tepidariorum" +} +item { + name: "202505" + id: 2015 + display_name: "Zosterops lateralis" +} +item { + name: "38671" + id: 2016 + display_name: "Aspidoscelis tigris" +} +item { + name: "38672" + id: 2017 + display_name: "Aspidoscelis tigris stejnegeri" +} +item { + name: "9176" + id: 2018 + display_name: "Zonotrichia leucophrys" +} +item { + name: "120596" + id: 2019 + display_name: "Aphonopelma hentzi" +} +item { + name: "9744" + id: 2020 + display_name: "Agelaius phoeniceus" +} +item { + name: "38684" + id: 2021 + display_name: "Aspidoscelis tigris mundus" +} +item { + name: "62426" + id: 2022 + display_name: "Aphantopus hyperantus" +} +item { + name: "30494" + id: 2023 + display_name: "Micrurus tener" +} +item { + name: "58578" + id: 2024 + display_name: "Euphydryas phaeton" +} +item { + name: "96036" + id: 2025 + display_name: "Brechmorhoga mendax" +} +item { + name: "333608" + id: 2026 + display_name: "Leukoma staminea" +} +item { + name: "38703" + id: 2027 + display_name: "Aspidoscelis sexlineata sexlineata" +} +item { + name: "126600" + id: 2028 + display_name: "Chortophaga viridifasciata" +} +item { + name: "63287" + id: 2029 + display_name: "Megalorchestia californiana" +} +item { + name: "128824" + id: 2030 + display_name: "Lucilia sericata" +} +item { + name: "104249" + id: 2031 + display_name: "Lepisosteus oculatus" +} +item { + name: "203153" + id: 2032 + display_name: "Parus major" +} +item { + name: "9183" + id: 2033 + display_name: "Zonotrichia capensis" +} +item { + name: "82201" + id: 2034 + display_name: "Hypena baltimoralis" +} +item { + name: "145217" + id: 2035 + display_name: "Oreothlypis peregrina" +} +item { + name: "145218" + id: 2036 + display_name: "Oreothlypis celata" +} +item { + name: "145221" + id: 2037 + display_name: "Oreothlypis ruficapilla" +} +item { + name: "145224" + id: 2038 + display_name: "Geothlypis philadelphia" +} +item { + name: "145225" + id: 2039 + display_name: "Geothlypis formosa" +} +item { + name: "448331" + id: 2040 + display_name: "Ambigolimax valentianus" +} +item { + name: "128845" + id: 2041 + display_name: "Copestylum mexicanum" +} +item { + name: "145231" + id: 2042 + display_name: "Setophaga tigrina" +} +item { + name: "145233" + id: 2043 + display_name: "Setophaga americana" +} +item { + name: "145235" + id: 2044 + display_name: "Setophaga magnolia" +} +item { + name: "145236" + id: 2045 + display_name: "Setophaga castanea" +} +item { + name: "145237" + id: 2046 + display_name: "Setophaga fusca" +} +item { + name: "145238" + id: 2047 + display_name: "Setophaga petechia" +} +item { + name: "145240" + id: 2048 + display_name: "Setophaga striata" +} +item { + name: "145242" + id: 2049 + display_name: "Setophaga palmarum" +} +item { + name: "179855" + id: 2050 + display_name: "Polites vibex" +} +item { + name: "145244" + id: 2051 + display_name: "Setophaga pinus" +} +item { + name: "145245" + id: 2052 + display_name: "Setophaga coronata" +} +item { + name: "145246" + id: 2053 + display_name: "Setophaga dominica" +} +item { + name: "5987" + id: 2054 + display_name: "Campylopterus hemileucurus" +} +item { + name: "17382" + id: 2055 + display_name: "Vireo cassinii" +} +item { + name: "145254" + id: 2056 + display_name: "Setophaga nigrescens" +} +item { + name: "145255" + id: 2057 + display_name: "Setophaga townsendi" +} +item { + name: "145256" + id: 2058 + display_name: "Setophaga occidentalis" +} +item { + name: "145257" + id: 2059 + display_name: "Setophaga chrysoparia" +} +item { + name: "145258" + id: 2060 + display_name: "Setophaga virens" +} +item { + name: "48786" + id: 2061 + display_name: "Pollicipes polymerus" +} +item { + name: "36207" + id: 2062 + display_name: "Sceloporus occidentalis longipes" +} +item { + name: "22392" + id: 2063 + display_name: "Eleutherodactylus marnockii" +} +item { + name: "22393" + id: 2064 + display_name: "Eleutherodactylus cystignathoides" +} +item { + name: "145275" + id: 2065 + display_name: "Cardellina canadensis" +} +item { + name: "145277" + id: 2066 + display_name: "Cardellina rubra" +} +item { + name: "7829" + id: 2067 + display_name: "Aphelocoma coerulescens" +} +item { + name: "41963" + id: 2068 + display_name: "Panthera pardus" +} +item { + name: "142998" + id: 2069 + display_name: "Pyrausta acrionalis" +} +item { + name: "18204" + id: 2070 + display_name: "Melanerpes erythrocephalus" +} +item { + name: "47425" + id: 2071 + display_name: "Tonicella lineata" +} +item { + name: "148460" + id: 2072 + display_name: "Charadra deridens" +} +item { + name: "145291" + id: 2073 + display_name: "Emberiza calandra" +} +item { + name: "52523" + id: 2074 + display_name: "Carcinus maenas" +} +item { + name: "46994" + id: 2075 + display_name: "Scapanus latimanus" +} +item { + name: "114314" + id: 2076 + display_name: "Tramea onusta" +} +item { + name: "145300" + id: 2077 + display_name: "Acanthis flammea" +} +item { + name: "63382" + id: 2078 + display_name: "Dermasterias imbricata" +} +item { + name: "126772" + id: 2079 + display_name: "Ursus americanus californiensis" +} +item { + name: "145304" + id: 2080 + display_name: "Spinus pinus" +} +item { + name: "10294" + id: 2081 + display_name: "Thraupis abbas" +} +item { + name: "145308" + id: 2082 + display_name: "Spinus psaltria" +} +item { + name: "145309" + id: 2083 + display_name: "Spinus lawrencei" +} +item { + name: "145310" + id: 2084 + display_name: "Spinus tristis" +} +item { + name: "3739" + id: 2085 + display_name: "Threskiornis aethiopicus" +} +item { + name: "47014" + id: 2086 + display_name: "Scalopus aquaticus" +} +item { + name: "4566" + id: 2087 + display_name: "Gygis alba" +} +item { + name: "43335" + id: 2088 + display_name: "Equus quagga" +} +item { + name: "41970" + id: 2089 + display_name: "Panthera onca" +} +item { + name: "128950" + id: 2090 + display_name: "Lycomorpha pholus" +} +item { + name: "11935" + id: 2091 + display_name: "Tachycineta bicolor" +} +item { + name: "333759" + id: 2092 + display_name: "Larus dominicanus dominicanus" +} +item { + name: "143008" + id: 2093 + display_name: "Herpetogramma pertextalis" +} +item { + name: "235341" + id: 2094 + display_name: "Coenonympha tullia california" +} +item { + name: "44705" + id: 2095 + display_name: "Mus musculus" +} +item { + name: "145352" + id: 2096 + display_name: "Lonchura oryzivora" +} +item { + name: "4840" + id: 2097 + display_name: "Haematopus palliatus" +} +item { + name: "244845" + id: 2098 + display_name: "Apiomerus californicus" +} +item { + name: "145360" + id: 2099 + display_name: "Chloris chloris" +} +item { + name: "5112" + id: 2100 + display_name: "Accipiter cooperii" +} +item { + name: "30675" + id: 2101 + display_name: "Agkistrodon piscivorus" +} +item { + name: "341972" + id: 2102 + display_name: "Crocodylus niloticus" +} +item { + name: "30677" + id: 2103 + display_name: "Agkistrodon piscivorus conanti" +} +item { + name: "30678" + id: 2104 + display_name: "Agkistrodon contortrix" +} +item { + name: "52900" + id: 2105 + display_name: "Caenurgina crassiuscula" +} +item { + name: "30682" + id: 2106 + display_name: "Agkistrodon contortrix laticinctus" +} +item { + name: "47067" + id: 2107 + display_name: "Bradypus variegatus" +} +item { + name: "55260" + id: 2108 + display_name: "Erythemis vesiculosa" +} +item { + name: "17402" + id: 2109 + display_name: "Vireo solitarius" +} +item { + name: "6369" + id: 2110 + display_name: "Selasphorus platycercus" +} +item { + name: "104416" + id: 2111 + display_name: "Lestes alacer" +} +item { + name: "128993" + id: 2112 + display_name: "Narceus annularus" +} +item { + name: "104422" + id: 2113 + display_name: "Lestes congener" +} +item { + name: "227307" + id: 2114 + display_name: "Patalene olyzonaria" +} +item { + name: "104429" + id: 2115 + display_name: "Lestes dryas" +} +item { + name: "194542" + id: 2116 + display_name: "Phyciodes graphica" +} +item { + name: "52904" + id: 2117 + display_name: "Microcrambus elegans" +} +item { + name: "129363" + id: 2118 + display_name: "Calephelis nemesis" +} +item { + name: "144506" + id: 2119 + display_name: "Chroicocephalus scopulinus" +} +item { + name: "30713" + id: 2120 + display_name: "Crotalus oreganus helleri" +} +item { + name: "47101" + id: 2121 + display_name: "Choloepus hoffmanni" +} +item { + name: "210942" + id: 2122 + display_name: "Caedicia simplex" +} +item { + name: "30719" + id: 2123 + display_name: "Crotalus scutulatus" +} +item { + name: "30724" + id: 2124 + display_name: "Crotalus ruber" +} +item { + name: "47110" + id: 2125 + display_name: "Triopha maculata" +} +item { + name: "4235" + id: 2126 + display_name: "Aechmophorus occidentalis" +} +item { + name: "30731" + id: 2127 + display_name: "Crotalus molossus" +} +item { + name: "30733" + id: 2128 + display_name: "Crotalus molossus nigrescens" +} +item { + name: "30735" + id: 2129 + display_name: "Crotalus mitchellii" +} +item { + name: "30740" + id: 2130 + display_name: "Crotalus lepidus" +} +item { + name: "30746" + id: 2131 + display_name: "Crotalus horridus" +} +item { + name: "63518" + id: 2132 + display_name: "Melanoplus differentialis" +} +item { + name: "30751" + id: 2133 + display_name: "Crotalus cerastes" +} +item { + name: "126640" + id: 2134 + display_name: "Caenurgina erechtea" +} +item { + name: "46086" + id: 2135 + display_name: "Marmota flaviventris" +} +item { + name: "194599" + id: 2136 + display_name: "Heliomata cycladata" +} +item { + name: "30764" + id: 2137 + display_name: "Crotalus atrox" +} +item { + name: "204520" + id: 2138 + display_name: "Hemiphaga novaeseelandiae" +} +item { + name: "128141" + id: 2139 + display_name: "Crepidula adunca" +} +item { + name: "121183" + id: 2140 + display_name: "Mythimna unipuncta" +} +item { + name: "40827" + id: 2141 + display_name: "Eidolon helvum" +} +item { + name: "4571" + id: 2142 + display_name: "Xema sabini" +} +item { + name: "211007" + id: 2143 + display_name: "Nepytia canosaria" +} +item { + name: "47171" + id: 2144 + display_name: "Flabellina iodinea" +} +item { + name: "211012" + id: 2145 + display_name: "Maliattha synochitis" +} +item { + name: "30798" + id: 2146 + display_name: "Bothrops asper" +} +item { + name: "47188" + id: 2147 + display_name: "Pachygrapsus crassipes" +} +item { + name: "55387" + id: 2148 + display_name: "Esox lucius" +} +item { + name: "58583" + id: 2149 + display_name: "Limenitis arthemis arthemis" +} +item { + name: "104548" + id: 2150 + display_name: "Leucorrhinia frigida" +} +item { + name: "104550" + id: 2151 + display_name: "Leucorrhinia hudsonica" +} +item { + name: "104551" + id: 2152 + display_name: "Leucorrhinia intacta" +} +item { + name: "47209" + id: 2153 + display_name: "Hermissenda crassicornis" +} +item { + name: "55655" + id: 2154 + display_name: "Lycaena phlaeas" +} +item { + name: "202861" + id: 2155 + display_name: "Otala lactea" +} +item { + name: "143037" + id: 2156 + display_name: "Lineodes integra" +} +item { + name: "47219" + id: 2157 + display_name: "Apis mellifera" +} +item { + name: "24254" + id: 2158 + display_name: "Pseudacris cadaverina" +} +item { + name: "47226" + id: 2159 + display_name: "Papilio rutulus" +} +item { + name: "104572" + id: 2160 + display_name: "Libellula comanche" +} +item { + name: "104574" + id: 2161 + display_name: "Libellula croceipennis" +} +item { + name: "104575" + id: 2162 + display_name: "Libellula cyanea" +} +item { + name: "145538" + id: 2163 + display_name: "Ovis canadensis canadensis" +} +item { + name: "104580" + id: 2164 + display_name: "Libellula incesta" +} +item { + name: "24257" + id: 2165 + display_name: "Pseudacris streckeri" +} +item { + name: "53866" + id: 2166 + display_name: "Calpodes ethlius" +} +item { + name: "18796" + id: 2167 + display_name: "Ramphastos sulfuratus" +} +item { + name: "2413" + id: 2168 + display_name: "Dacelo novaeguineae" +} +item { + name: "482" + id: 2169 + display_name: "Fulica atra" +} +item { + name: "47251" + id: 2170 + display_name: "Sphyraena barracuda" +} +item { + name: "358549" + id: 2171 + display_name: "Hemaris diffinis" +} +item { + name: "81526" + id: 2172 + display_name: "Crotalus viridis" +} +item { + name: "342169" + id: 2173 + display_name: "Hirundo rustica erythrogaster" +} +item { + name: "39280" + id: 2174 + display_name: "Leiocephalus carinatus" +} +item { + name: "47269" + id: 2175 + display_name: "Dasyatis americana" +} +item { + name: "55467" + id: 2176 + display_name: "Sabulodes aegrotata" +} +item { + name: "6316" + id: 2177 + display_name: "Calypte costae" +} +item { + name: "6317" + id: 2178 + display_name: "Calypte anna" +} +item { + name: "47280" + id: 2179 + display_name: "Pterois volitans" +} +item { + name: "81608" + id: 2180 + display_name: "Geukensia demissa" +} +item { + name: "121012" + id: 2181 + display_name: "Euglandina rosea" +} +item { + name: "236980" + id: 2182 + display_name: "Colaptes auratus cafer" +} +item { + name: "38673" + id: 2183 + display_name: "Aspidoscelis tigris tigris" +} +item { + name: "3786" + id: 2184 + display_name: "Sula nebouxii" +} +item { + name: "55487" + id: 2185 + display_name: "Diabrotica undecimpunctata" +} +item { + name: "243904" + id: 2186 + display_name: "Phrynosoma platyrhinos" +} +item { + name: "55489" + id: 2187 + display_name: "Cycloneda munda" +} +item { + name: "204491" + id: 2188 + display_name: "Copsychus saularis" +} +item { + name: "55492" + id: 2189 + display_name: "Cycloneda polita" +} +item { + name: "129222" + id: 2190 + display_name: "Heterophleps triguttaria" +} +item { + name: "129223" + id: 2191 + display_name: "Pasiphila rectangulata" +} +item { + name: "28365" + id: 2192 + display_name: "Thamnophis sirtalis sirtalis" +} +item { + name: "47316" + id: 2193 + display_name: "Chaetodon lunula" +} +item { + name: "6359" + id: 2194 + display_name: "Selasphorus sasin" +} +item { + name: "62500" + id: 2195 + display_name: "Leptophobia aripa" +} +item { + name: "6363" + id: 2196 + display_name: "Selasphorus rufus" +} +item { + name: "96480" + id: 2197 + display_name: "Calopteryx aequabilis" +} +item { + name: "55521" + id: 2198 + display_name: "Papilio eurymedon" +} +item { + name: "6371" + id: 2199 + display_name: "Calothorax lucifer" +} +item { + name: "129263" + id: 2200 + display_name: "Syrbula admirabilis" +} +item { + name: "28371" + id: 2201 + display_name: "Thamnophis sirtalis fitchi" +} +item { + name: "243962" + id: 2202 + display_name: "Charina bottae" +} +item { + name: "145659" + id: 2203 + display_name: "Acronicta americana" +} +item { + name: "14588" + id: 2204 + display_name: "Pycnonotus barbatus" +} +item { + name: "480298" + id: 2205 + display_name: "Cornu aspersum" +} +item { + name: "51584" + id: 2206 + display_name: "Melanitis leda" +} +item { + name: "243970" + id: 2207 + display_name: "Larus glaucescens \303\227 occidentalis" +} +item { + name: "55556" + id: 2208 + display_name: "Oncopeltus fasciatus" +} +item { + name: "506117" + id: 2209 + display_name: "Aphelocoma woodhouseii" +} +item { + name: "63750" + id: 2210 + display_name: "Anavitrinella pampinaria" +} +item { + name: "30983" + id: 2211 + display_name: "Sistrurus miliarius" +} +item { + name: "211210" + id: 2212 + display_name: "Holocnemus pluchei" +} +item { + name: "49587" + id: 2213 + display_name: "Micropterus salmoides" +} +item { + name: "6417" + id: 2214 + display_name: "Florisuga mellivora" +} +item { + name: "47381" + id: 2215 + display_name: "Latrodectus mactans" +} +item { + name: "47382" + id: 2216 + display_name: "Latrodectus hesperus" +} +item { + name: "4851" + id: 2217 + display_name: "Haematopus finschi" +} +item { + name: "51588" + id: 2218 + display_name: "Papilio polytes" +} +item { + name: "144431" + id: 2219 + display_name: "Falcipennis canadensis" +} +item { + name: "118490" + id: 2220 + display_name: "Haematopis grataria" +} +item { + name: "6433" + id: 2221 + display_name: "Archilochus alexandri" +} +item { + name: "52956" + id: 2222 + display_name: "Chaetodon capistratus" +} +item { + name: "203050" + id: 2223 + display_name: "Junonia genoveva" +} +item { + name: "5170" + id: 2224 + display_name: "Circus cyaneus" +} +item { + name: "84332" + id: 2225 + display_name: "Panorpa nuptialis" +} +item { + name: "47414" + id: 2226 + display_name: "Emerita analoga" +} +item { + name: "129335" + id: 2227 + display_name: "Gibbifer californicus" +} +item { + name: "55610" + id: 2228 + display_name: "Pyrrhocoris apterus" +} +item { + name: "58421" + id: 2229 + display_name: "Phidippus johnsoni" +} +item { + name: "208608" + id: 2230 + display_name: "Trachymela sloanei" +} +item { + name: "68138" + id: 2231 + display_name: "Sympetrum corruptum" +} +item { + name: "129350" + id: 2232 + display_name: "Photinus pyralis" +} +item { + name: "55625" + id: 2233 + display_name: "Sympetrum striolatum" +} +item { + name: "55626" + id: 2234 + display_name: "Pieris rapae" +} +item { + name: "203084" + id: 2235 + display_name: "Ardea alba modesta" +} +item { + name: "129362" + id: 2236 + display_name: "Zerene cesonia" +} +item { + name: "55638" + id: 2237 + display_name: "Anania hortulata" +} +item { + name: "148537" + id: 2238 + display_name: "Astraptes fulgerator" +} +item { + name: "55640" + id: 2239 + display_name: "Celastrina argiolus" +} +item { + name: "55641" + id: 2240 + display_name: "Polyommatus icarus" +} +item { + name: "16028" + id: 2241 + display_name: "Myiarchus crinitus" +} +item { + name: "55643" + id: 2242 + display_name: "Araschnia levana" +} +item { + name: "121180" + id: 2243 + display_name: "Megastraea undosa" +} +item { + name: "47454" + id: 2244 + display_name: "Triopha catalinae" +} +item { + name: "28389" + id: 2245 + display_name: "Thamnophis ordinoides" +} +item { + name: "68139" + id: 2246 + display_name: "Sympetrum vicinum" +} +item { + name: "55651" + id: 2247 + display_name: "Autographa gamma" +} +item { + name: "55653" + id: 2248 + display_name: "Maniola jurtina" +} +item { + name: "84369" + id: 2249 + display_name: "Libellula forensis" +} +item { + name: "47135" + id: 2250 + display_name: "Badumna longinqua" +} +item { + name: "48213" + id: 2251 + display_name: "Ariolimax californicus" +} +item { + name: "121196" + id: 2252 + display_name: "Acanthurus coeruleus" +} +item { + name: "47469" + id: 2253 + display_name: "Doris montereyensis" +} +item { + name: "5181" + id: 2254 + display_name: "Buteo regalis" +} +item { + name: "47472" + id: 2255 + display_name: "Acanthodoris lutea" +} +item { + name: "129415" + id: 2256 + display_name: "Copaeodes aurantiaca" +} +item { + name: "47505" + id: 2257 + display_name: "Geitodoris heathi" +} +item { + name: "28398" + id: 2258 + display_name: "Thamnophis elegans" +} +item { + name: "6553" + id: 2259 + display_name: "Aeronautes saxatalis" +} +item { + name: "47516" + id: 2260 + display_name: "Oncorhynchus mykiss" +} +item { + name: "6557" + id: 2261 + display_name: "Chaetura vauxi" +} +item { + name: "47518" + id: 2262 + display_name: "Salmo trutta" +} +item { + name: "55711" + id: 2263 + display_name: "Ladona depressa" +} +item { + name: "55719" + id: 2264 + display_name: "Eristalis tenax" +} +item { + name: "6571" + id: 2265 + display_name: "Chaetura pelagica" +} +item { + name: "119881" + id: 2266 + display_name: "Chrysochus cobaltinus" +} +item { + name: "145239" + id: 2267 + display_name: "Setophaga pensylvanica" +} +item { + name: "154043" + id: 2268 + display_name: "Bombus huntii" +} +item { + name: "41955" + id: 2269 + display_name: "Acinonyx jubatus" +} +item { + name: "55746" + id: 2270 + display_name: "Misumena vatia" +} +item { + name: "12024" + id: 2271 + display_name: "Lanius ludovicianus" +} +item { + name: "5063" + id: 2272 + display_name: "Anhinga anhinga" +} +item { + name: "59892" + id: 2273 + display_name: "Prionus californicus" +} +item { + name: "52986" + id: 2274 + display_name: "Largus californicus" +} +item { + name: "204454" + id: 2275 + display_name: "Acridotheres tristis" +} +item { + name: "14816" + id: 2276 + display_name: "Sitta pygmaea" +} +item { + name: "148560" + id: 2277 + display_name: "Mestra amymone" +} +item { + name: "4585" + id: 2278 + display_name: "Actophilornis africanus" +} +item { + name: "47590" + id: 2279 + display_name: "Phloeodes diabolicus" +} +item { + name: "14823" + id: 2280 + display_name: "Sitta canadensis" +} +item { + name: "14824" + id: 2281 + display_name: "Sitta europaea" +} +item { + name: "14825" + id: 2282 + display_name: "Sitta pusilla" +} +item { + name: "67598" + id: 2283 + display_name: "Solenopsis invicta" +} +item { + name: "6638" + id: 2284 + display_name: "Apus apus" +} +item { + name: "301557" + id: 2285 + display_name: "Euphoria basalis" +} +item { + name: "132070" + id: 2286 + display_name: "Phaneroptera nana" +} +item { + name: "14850" + id: 2287 + display_name: "Sturnus vulgaris" +} +item { + name: "62550" + id: 2288 + display_name: "Seiurus aurocapilla" +} +item { + name: "64006" + id: 2289 + display_name: "Corbicula fluminea" +} +item { + name: "204545" + id: 2290 + display_name: "Motacilla flava" +} +item { + name: "47632" + id: 2291 + display_name: "Katharina tunicata" +} +item { + name: "325309" + id: 2292 + display_name: "Chortophaga viridifasciata viridifasciata" +} +item { + name: "104993" + id: 2293 + display_name: "Macrodiplax balteata" +} +item { + name: "17408" + id: 2294 + display_name: "Vireo griseus" +} +item { + name: "14895" + id: 2295 + display_name: "Toxostoma longirostre" +} +item { + name: "47664" + id: 2296 + display_name: "Henricia leviuscula" +} +item { + name: "31281" + id: 2297 + display_name: "Calotes versicolor" +} +item { + name: "119086" + id: 2298 + display_name: "Agrius cingulata" +} +item { + name: "3849" + id: 2299 + display_name: "Calidris alba" +} +item { + name: "14906" + id: 2300 + display_name: "Toxostoma redivivum" +} +item { + name: "144479" + id: 2301 + display_name: "Gallinula galeata" +} +item { + name: "3850" + id: 2302 + display_name: "Calidris himantopus" +} +item { + name: "117520" + id: 2303 + display_name: "Enhydra lutris nereis" +} +item { + name: "51491" + id: 2304 + display_name: "Myliobatis californica" +} +item { + name: "121612" + id: 2305 + display_name: "Estigmene acrea" +} +item { + name: "105034" + id: 2306 + display_name: "Macromia illinoiensis" +} +item { + name: "6498" + id: 2307 + display_name: "Eugenes fulgens" +} +item { + name: "46179" + id: 2308 + display_name: "Cynomys ludovicianus" +} +item { + name: "105049" + id: 2309 + display_name: "Macromia taeniolata" +} +item { + name: "94045" + id: 2310 + display_name: "Anax longipes" +} +item { + name: "143119" + id: 2311 + display_name: "Galgula partita" +} +item { + name: "9317" + id: 2312 + display_name: "Icterus wagleri" +} +item { + name: "122704" + id: 2313 + display_name: "Nucella ostrina" +} +item { + name: "146709" + id: 2314 + display_name: "Grylloprociphilus imbricator" +} +item { + name: "9318" + id: 2315 + display_name: "Icterus parisorum" +} +item { + name: "85333" + id: 2316 + display_name: "Micrathena gracilis" +} +item { + name: "126737" + id: 2317 + display_name: "Anania funebris" +} +item { + name: "49053" + id: 2318 + display_name: "Cryptochiton stelleri" +} +item { + name: "47721" + id: 2319 + display_name: "Parastichopus californicus" +} +item { + name: "34050" + id: 2320 + display_name: "Phelsuma laticauda" +} +item { + name: "154219" + id: 2321 + display_name: "Notarctia proxima" +} +item { + name: "51781" + id: 2322 + display_name: "Tyria jacobaeae" +} +item { + name: "24230" + id: 2323 + display_name: "Acris crepitans" +} +item { + name: "146032" + id: 2324 + display_name: "Coluber flagellum" +} +item { + name: "146033" + id: 2325 + display_name: "Coluber flagellum flagellum" +} +item { + name: "244340" + id: 2326 + display_name: "Hordnia atropunctata" +} +item { + name: "146037" + id: 2327 + display_name: "Coluber taeniatus" +} +item { + name: "244344" + id: 2328 + display_name: "Scopula rubraria" +} +item { + name: "47737" + id: 2329 + display_name: "Harpaphe haydeniana" +} +item { + name: "5227" + id: 2330 + display_name: "Buteo platypterus" +} +item { + name: "39556" + id: 2331 + display_name: "Apalone spinifera" +} +item { + name: "39560" + id: 2332 + display_name: "Apalone spinifera emoryi" +} +item { + name: "318836" + id: 2333 + display_name: "Gallinago gallinago" +} +item { + name: "105098" + id: 2334 + display_name: "Magicicada septendecim" +} +item { + name: "96907" + id: 2335 + display_name: "Celithemis fasciata" +} +item { + name: "9325" + id: 2336 + display_name: "Icterus spurius" +} +item { + name: "3864" + id: 2337 + display_name: "Calidris minutilla" +} +item { + name: "14995" + id: 2338 + display_name: "Dumetella carolinensis" +} +item { + name: "424597" + id: 2339 + display_name: "Porphyrio hochstetteri" +} +item { + name: "47768" + id: 2340 + display_name: "Doriopsilla albopunctata" +} +item { + name: "498116" + id: 2341 + display_name: "Aeolidia papillosa" +} +item { + name: "244378" + id: 2342 + display_name: "Mallophora fautrix" +} +item { + name: "3866" + id: 2343 + display_name: "Calidris fuscicollis" +} +item { + name: "47776" + id: 2344 + display_name: "Ariolimax columbianus" +} +item { + name: "144497" + id: 2345 + display_name: "Phalaropus tricolor" +} +item { + name: "39824" + id: 2346 + display_name: "Pseudemys nelsoni" +} +item { + name: "236979" + id: 2347 + display_name: "Colaptes auratus auratus" +} +item { + name: "55990" + id: 2348 + display_name: "Podarcis muralis" +} +item { + name: "244407" + id: 2349 + display_name: "Zelus renardii" +} +item { + name: "47802" + id: 2350 + display_name: "Lymantria dispar" +} +item { + name: "15035" + id: 2351 + display_name: "Melanotis caerulescens" +} +item { + name: "51658" + id: 2352 + display_name: "Anthopleura artemisia" +} +item { + name: "121534" + id: 2353 + display_name: "Oreta rosea" +} +item { + name: "73504" + id: 2354 + display_name: "Tiaris olivaceus" +} +item { + name: "15045" + id: 2355 + display_name: "Oreoscoptes montanus" +} +item { + name: "3873" + id: 2356 + display_name: "Limnodromus scolopaceus" +} +item { + name: "47673" + id: 2357 + display_name: "Pycnopodia helianthoides" +} +item { + name: "47817" + id: 2358 + display_name: "Libellula saturata" +} +item { + name: "56644" + id: 2359 + display_name: "Polygonia satyrus" +} +item { + name: "47826" + id: 2360 + display_name: "Cancer productus" +} +item { + name: "3875" + id: 2361 + display_name: "Tringa solitaria" +} +item { + name: "39782" + id: 2362 + display_name: "Trachemys scripta" +} +item { + name: "143140" + id: 2363 + display_name: "Cyllopsis gemma" +} +item { + name: "29818" + id: 2364 + display_name: "Lampropeltis holbrooki" +} +item { + name: "56293" + id: 2365 + display_name: "Macroglossum stellatarum" +} +item { + name: "154340" + id: 2366 + display_name: "Gryllodes sigillatus" +} +item { + name: "14801" + id: 2367 + display_name: "Sitta carolinensis" +} +item { + name: "121578" + id: 2368 + display_name: "Ovis aries" +} +item { + name: "3879" + id: 2369 + display_name: "Tringa totanus" +} +item { + name: "6893" + id: 2370 + display_name: "Dendrocygna autumnalis" +} +item { + name: "154353" + id: 2371 + display_name: "Sunira bicolorago" +} +item { + name: "6898" + id: 2372 + display_name: "Dendrocygna viduata" +} +item { + name: "6899" + id: 2373 + display_name: "Dendrocygna bicolor" +} +item { + name: "9342" + id: 2374 + display_name: "Icterus abeillei" +} +item { + name: "39670" + id: 2375 + display_name: "Lepidochelys olivacea" +} +item { + name: "4867" + id: 2376 + display_name: "Vanellus chilensis" +} +item { + name: "39677" + id: 2377 + display_name: "Dermochelys coriacea" +} +item { + name: "113407" + id: 2378 + display_name: "Stylurus plagiatus" +} +item { + name: "39682" + id: 2379 + display_name: "Chelydra serpentina" +} +item { + name: "6915" + id: 2380 + display_name: "Cygnus buccinator" +} +item { + name: "6916" + id: 2381 + display_name: "Cygnus cygnus" +} +item { + name: "6917" + id: 2382 + display_name: "Cygnus columbianus" +} +item { + name: "29825" + id: 2383 + display_name: "Lampropeltis calligaster calligaster" +} +item { + name: "6921" + id: 2384 + display_name: "Cygnus olor" +} +item { + name: "146186" + id: 2385 + display_name: "Intellagama lesueurii" +} +item { + name: "9346" + id: 2386 + display_name: "Icterus galbula" +} +item { + name: "126765" + id: 2387 + display_name: "Plutella xylostella" +} +item { + name: "71154" + id: 2388 + display_name: "Aphis nerii" +} +item { + name: "6930" + id: 2389 + display_name: "Anas platyrhynchos" +} +item { + name: "6933" + id: 2390 + display_name: "Anas acuta" +} +item { + name: "39703" + id: 2391 + display_name: "Sternotherus odoratus" +} +item { + name: "6937" + id: 2392 + display_name: "Anas crecca" +} +item { + name: "64287" + id: 2393 + display_name: "Lottia digitalis" +} +item { + name: "6944" + id: 2394 + display_name: "Anas cyanoptera" +} +item { + name: "39713" + id: 2395 + display_name: "Kinosternon subrubrum" +} +item { + name: "26691" + id: 2396 + display_name: "Scaphiopus couchii" +} +item { + name: "6948" + id: 2397 + display_name: "Anas fulvigula" +} +item { + name: "6953" + id: 2398 + display_name: "Anas discors" +} +item { + name: "47914" + id: 2399 + display_name: "Eumorpha pandorus" +} +item { + name: "47916" + id: 2400 + display_name: "Actias luna" +} +item { + name: "6957" + id: 2401 + display_name: "Anas strepera" +} +item { + name: "47919" + id: 2402 + display_name: "Antheraea polyphemus" +} +item { + name: "119953" + id: 2403 + display_name: "Hypoprepia fucosa" +} +item { + name: "6961" + id: 2404 + display_name: "Anas clypeata" +} +item { + name: "134119" + id: 2405 + display_name: "Anisomorpha buprestoides" +} +item { + name: "51678" + id: 2406 + display_name: "Coenagrion puella" +} +item { + name: "72502" + id: 2407 + display_name: "Anas chlorotis" +} +item { + name: "49060" + id: 2408 + display_name: "Epiactis prolifera" +} +item { + name: "42122" + id: 2409 + display_name: "Phacochoerus africanus" +} +item { + name: "58507" + id: 2410 + display_name: "Poanes hobomok" +} +item { + name: "121669" + id: 2411 + display_name: "Stenopus hispidus" +} +item { + name: "8143" + id: 2412 + display_name: "Rhipidura leucophrys" +} +item { + name: "6985" + id: 2413 + display_name: "Anas americana" +} +item { + name: "6993" + id: 2414 + display_name: "Bucephala albeola" +} +item { + name: "121682" + id: 2415 + display_name: "Tetraclita rubescens" +} +item { + name: "6996" + id: 2416 + display_name: "Mergus serrator" +} +item { + name: "113498" + id: 2417 + display_name: "Sympetrum ambiguum" +} +item { + name: "39771" + id: 2418 + display_name: "Chrysemys picta" +} +item { + name: "7004" + id: 2419 + display_name: "Mergus merganser" +} +item { + name: "39773" + id: 2420 + display_name: "Chrysemys picta bellii" +} +item { + name: "113503" + id: 2421 + display_name: "Sympetrum danae" +} +item { + name: "113507" + id: 2422 + display_name: "Sympetrum fonscolombii" +} +item { + name: "154469" + id: 2423 + display_name: "Isa textula" +} +item { + name: "47975" + id: 2424 + display_name: "Argia apicalis" +} +item { + name: "7018" + id: 2425 + display_name: "Anser anser" +} +item { + name: "7019" + id: 2426 + display_name: "Anser albifrons" +} +item { + name: "47980" + id: 2427 + display_name: "Speyeria cybele" +} +item { + name: "58514" + id: 2428 + display_name: "Euphyes vestris" +} +item { + name: "113519" + id: 2429 + display_name: "Sympetrum obtrusum" +} +item { + name: "7024" + id: 2430 + display_name: "Somateria mollissima" +} +item { + name: "39793" + id: 2431 + display_name: "Trachemys scripta scripta" +} +item { + name: "367475" + id: 2432 + display_name: "Rallus obsoletus" +} +item { + name: "121716" + id: 2433 + display_name: "Uresiphita reversalis" +} +item { + name: "113525" + id: 2434 + display_name: "Sympetrum sanguineum" +} +item { + name: "113526" + id: 2435 + display_name: "Sympetrum semicinctum" +} +item { + name: "18921" + id: 2436 + display_name: "Platycercus elegans" +} +item { + name: "7032" + id: 2437 + display_name: "Melanitta fusca" +} +item { + name: "5268" + id: 2438 + display_name: "Milvus migrans" +} +item { + name: "144536" + id: 2439 + display_name: "Gelochelidon nilotica" +} +item { + name: "413503" + id: 2440 + display_name: "Ninox novaeseelandiae novaeseelandiae" +} +item { + name: "7036" + id: 2441 + display_name: "Melanitta perspicillata" +} +item { + name: "64382" + id: 2442 + display_name: "Lissotriton vulgaris" +} +item { + name: "39807" + id: 2443 + display_name: "Terrapene ornata" +} +item { + name: "39808" + id: 2444 + display_name: "Terrapene ornata luteola" +} +item { + name: "7044" + id: 2445 + display_name: "Aythya collaris" +} +item { + name: "7045" + id: 2446 + display_name: "Aythya ferina" +} +item { + name: "7046" + id: 2447 + display_name: "Aythya fuligula" +} +item { + name: "146314" + id: 2448 + display_name: "Opheodrys vernalis" +} +item { + name: "3906" + id: 2449 + display_name: "Numenius americanus" +} +item { + name: "39823" + id: 2450 + display_name: "Pseudemys gorzugi" +} +item { + name: "178991" + id: 2451 + display_name: "Sypharochiton pelliserpentis" +} +item { + name: "7061" + id: 2452 + display_name: "Chen caerulescens" +} +item { + name: "39830" + id: 2453 + display_name: "Pseudemys concinna" +} +item { + name: "127490" + id: 2454 + display_name: "Parrhasius m-album" +} +item { + name: "15256" + id: 2455 + display_name: "Chamaea fasciata" +} +item { + name: "39836" + id: 2456 + display_name: "Malaclemys terrapin" +} +item { + name: "133764" + id: 2457 + display_name: "Trichopoda pennipes" +} +item { + name: "334753" + id: 2458 + display_name: "Hypselonotus punctiventris" +} +item { + name: "58611" + id: 2459 + display_name: "Amia calva" +} +item { + name: "56240" + id: 2460 + display_name: "Argia vivida" +} +item { + name: "7089" + id: 2461 + display_name: "Branta canadensis" +} +item { + name: "146354" + id: 2462 + display_name: "Phrynosoma blainvillii" +} +item { + name: "56243" + id: 2463 + display_name: "Plebejus acmon" +} +item { + name: "144542" + id: 2464 + display_name: "Thalasseus elegans" +} +item { + name: "121783" + id: 2465 + display_name: "Lithobates clamitans melanota" +} +item { + name: "39865" + id: 2466 + display_name: "Glyptemys insculpta" +} +item { + name: "39867" + id: 2467 + display_name: "Emys orbicularis" +} +item { + name: "7104" + id: 2468 + display_name: "Branta sandvicensis" +} +item { + name: "50336" + id: 2469 + display_name: "Siproeta stelenes" +} +item { + name: "7056" + id: 2470 + display_name: "Aythya americana" +} +item { + name: "7107" + id: 2471 + display_name: "Aix sponsa" +} +item { + name: "7109" + id: 2472 + display_name: "Lophodytes cucullatus" +} +item { + name: "7111" + id: 2473 + display_name: "Histrionicus histrionicus" +} +item { + name: "367562" + id: 2474 + display_name: "Aratinga nenday" +} +item { + name: "39885" + id: 2475 + display_name: "Emydoidea blandingii" +} +item { + name: "367566" + id: 2476 + display_name: "Psittacara holochlorus" +} +item { + name: "143181" + id: 2477 + display_name: "Marimatha nigrofimbria" +} +item { + name: "7120" + id: 2478 + display_name: "Cairina moschata" +} +item { + name: "7122" + id: 2479 + display_name: "Netta rufina" +} +item { + name: "130003" + id: 2480 + display_name: "Phaeoura quernaria" +} +item { + name: "367572" + id: 2481 + display_name: "Psittacara erythrogenys" +} +item { + name: "17009" + id: 2482 + display_name: "Sayornis saya" +} +item { + name: "154582" + id: 2483 + display_name: "Ennomos magnaria" +} +item { + name: "58532" + id: 2484 + display_name: "Colias eurytheme" +} +item { + name: "121821" + id: 2485 + display_name: "Sceliphron caementarium" +} +item { + name: "48094" + id: 2486 + display_name: "Dryocampa rubicunda" +} +item { + name: "7057" + id: 2487 + display_name: "Aythya valisineria" +} +item { + name: "17646" + id: 2488 + display_name: "Picoides albolarvatus" +} +item { + name: "201551" + id: 2489 + display_name: "Procyon lotor lotor" +} +item { + name: "58534" + id: 2490 + display_name: "Lycaena hyllus" +} +item { + name: "73553" + id: 2491 + display_name: "Vermivora cyanoptera" +} +item { + name: "359401" + id: 2492 + display_name: "Exomala orientalis" +} +item { + name: "8018" + id: 2493 + display_name: "Corvus caurinus" +} +item { + name: "490478" + id: 2494 + display_name: "Tegula brunnea" +} +item { + name: "20307" + id: 2495 + display_name: "Asio otus" +} +item { + name: "227466" + id: 2496 + display_name: "Peridea ferruginea" +} +item { + name: "122172" + id: 2497 + display_name: "Pyrisitia lisa" +} +item { + name: "133631" + id: 2498 + display_name: "Polites peckius" +} +item { + name: "8021" + id: 2499 + display_name: "Corvus brachyrhynchos" +} +item { + name: "7170" + id: 2500 + display_name: "Clangula hyemalis" +} +item { + name: "58539" + id: 2501 + display_name: "Satyrium calanus" +} +item { + name: "27137" + id: 2502 + display_name: "Coluber constrictor" +} +item { + name: "7176" + id: 2503 + display_name: "Chenonetta jubata" +} +item { + name: "42157" + id: 2504 + display_name: "Giraffa camelopardalis" +} +item { + name: "144541" + id: 2505 + display_name: "Thalasseus sandvicensis" +} +item { + name: "23572" + id: 2506 + display_name: "Litoria aurea" +} +item { + name: "354820" + id: 2507 + display_name: "Patiriella regularis" +} +item { + name: "55887" + id: 2508 + display_name: "Andricus quercuscalifornicus" +} +item { + name: "46255" + id: 2509 + display_name: "Ammospermophilus leucurus" +} +item { + name: "334341" + id: 2510 + display_name: "Oryctolagus cuniculus domesticus" +} +item { + name: "144560" + id: 2511 + display_name: "Eolophus roseicapilla" +} +item { + name: "94043" + id: 2512 + display_name: "Anax imperator" +} +item { + name: "425004" + id: 2513 + display_name: "Dryas iulia moderata" +} +item { + name: "269359" + id: 2514 + display_name: "Cactophagus spinolae" +} +item { + name: "72755" + id: 2515 + display_name: "Colaptes rubiginosus" +} +item { + name: "319123" + id: 2516 + display_name: "Meleagris gallopavo silvestris" +} +item { + name: "130846" + id: 2517 + display_name: "Lyssa zampa" +} +item { + name: "203831" + id: 2518 + display_name: "Nemoria bistriaria" +} +item { + name: "367678" + id: 2519 + display_name: "Ptiliogonys cinereus" +} +item { + name: "5301" + id: 2520 + display_name: "Elanoides forficatus" +} +item { + name: "9398" + id: 2521 + display_name: "Carduelis carduelis" +} +item { + name: "143201" + id: 2522 + display_name: "Coryphista meadii" +} +item { + name: "104419" + id: 2523 + display_name: "Lestes australis" +} +item { + name: "367693" + id: 2524 + display_name: "Cassiculus melanicterus" +} +item { + name: "143452" + id: 2525 + display_name: "Deidamia inscriptum" +} +item { + name: "466003" + id: 2526 + display_name: "Romalea microptera" +} +item { + name: "84494" + id: 2527 + display_name: "Paraphidippus aurantius" +} +item { + name: "203866" + id: 2528 + display_name: "Rabdophaga strobiloides" +} +item { + name: "72797" + id: 2529 + display_name: "Dendragapus fuliginosus" +} +item { + name: "7266" + id: 2530 + display_name: "Psaltriparus minimus" +} +item { + name: "120920" + id: 2531 + display_name: "Odocoileus virginianus clavium" +} +item { + name: "7278" + id: 2532 + display_name: "Aegithalos caudatus" +} +item { + name: "30681" + id: 2533 + display_name: "Agkistrodon contortrix mokasen" +} +item { + name: "413547" + id: 2534 + display_name: "Zosterops lateralis lateralis" +} +item { + name: "48262" + id: 2535 + display_name: "Apatelodes torrefacta" +} +item { + name: "121993" + id: 2536 + display_name: "Lampides boeticus" +} +item { + name: "48267" + id: 2537 + display_name: "Crotalus oreganus oreganus" +} +item { + name: "48268" + id: 2538 + display_name: "Crotalus oreganus" +} +item { + name: "147309" + id: 2539 + display_name: "Feltia herilis" +} +item { + name: "146413" + id: 2540 + display_name: "Sceloporus consobrinus" +} +item { + name: "326764" + id: 2541 + display_name: "Cyprinus carpio haematopterus" +} +item { + name: "5315" + id: 2542 + display_name: "Haliaeetus leucogaster" +} +item { + name: "4519" + id: 2543 + display_name: "Uria aalge" +} +item { + name: "40085" + id: 2544 + display_name: "Gopherus polyphemus" +} +item { + name: "23702" + id: 2545 + display_name: "Agalychnis callidryas" +} +item { + name: "210116" + id: 2546 + display_name: "Tringa semipalmata inornatus" +} +item { + name: "40092" + id: 2547 + display_name: "Stigmochelys pardalis" +} +item { + name: "59931" + id: 2548 + display_name: "Acanthurus triostegus" +} +item { + name: "48292" + id: 2549 + display_name: "Philoscia muscorum" +} +item { + name: "146601" + id: 2550 + display_name: "Scolopendra heros" +} +item { + name: "244906" + id: 2551 + display_name: "Panchlora nivea" +} +item { + name: "48302" + id: 2552 + display_name: "Limulus polyphemus" +} +item { + name: "180008" + id: 2553 + display_name: "Otospermophilus variegatus" +} +item { + name: "7347" + id: 2554 + display_name: "Alauda arvensis" +} +item { + name: "43459" + id: 2555 + display_name: "Macaca fascicularis" +} +item { + name: "113846" + id: 2556 + display_name: "Telebasis salva" +} +item { + name: "7356" + id: 2557 + display_name: "Galerida cristata" +} +item { + name: "64705" + id: 2558 + display_name: "Delichon urbicum" +} +item { + name: "145932" + id: 2559 + display_name: "Aspidoscelis hyperythra beldingi" +} +item { + name: "72912" + id: 2560 + display_name: "Helmitheros vermivorum" +} +item { + name: "69805" + id: 2561 + display_name: "Octogomphus specularis" +} +item { + name: "129572" + id: 2562 + display_name: "Aphomia sociella" +} +item { + name: "31964" + id: 2563 + display_name: "Barisia imbricata" +} +item { + name: "244625" + id: 2564 + display_name: "Halmus chalybeus" +} +item { + name: "58576" + id: 2565 + display_name: "Phyciodes cocyta" +} +item { + name: "72931" + id: 2566 + display_name: "Hylocharis leucotis" +} +item { + name: "104449" + id: 2567 + display_name: "Lestes rectangularis" +} +item { + name: "14886" + id: 2568 + display_name: "Mimus polyglottos" +} +item { + name: "23783" + id: 2569 + display_name: "Hyla versicolor" +} +item { + name: "23784" + id: 2570 + display_name: "Hyla plicata" +} +item { + name: "8575" + id: 2571 + display_name: "Gymnorhina tibicen" +} +item { + name: "2599" + id: 2572 + display_name: "Alcedo atthis" +} +item { + name: "61152" + id: 2573 + display_name: "Pyrrhosoma nymphula" +} +item { + name: "58579" + id: 2574 + display_name: "Polygonia interrogationis" +} +item { + name: "31993" + id: 2575 + display_name: "Ophisaurus attenuatus attenuatus" +} +item { + name: "53985" + id: 2576 + display_name: "Odocoileus hemionus californicus" +} +item { + name: "144549" + id: 2577 + display_name: "Streptopelia chinensis" +} +item { + name: "105730" + id: 2578 + display_name: "Micrathyria hagenii" +} +item { + name: "7428" + id: 2579 + display_name: "Bombycilla cedrorum" +} +item { + name: "7429" + id: 2580 + display_name: "Bombycilla garrulus" +} +item { + name: "50391" + id: 2581 + display_name: "Polygonia gracilis" +} +item { + name: "7067" + id: 2582 + display_name: "Tadorna tadorna" +} +item { + name: "413513" + id: 2583 + display_name: "Petroica australis australis" +} +item { + name: "39469" + id: 2584 + display_name: "Varanus varius" +} +item { + name: "58479" + id: 2585 + display_name: "Pholisora catullus" +} +item { + name: "127929" + id: 2586 + display_name: "Achalarus lyciades" +} +item { + name: "48403" + id: 2587 + display_name: "Gasterosteus aculeatus" +} +item { + name: "18990" + id: 2588 + display_name: "Amazona autumnalis" +} +item { + name: "1241" + id: 2589 + display_name: "Dendragapus obscurus" +} +item { + name: "228634" + id: 2590 + display_name: "Ponometia erastrioides" +} +item { + name: "64806" + id: 2591 + display_name: "Pelophylax" +} +item { + name: "51761" + id: 2592 + display_name: "Hetaerina americana" +} +item { + name: "7464" + id: 2593 + display_name: "Catherpes mexicanus" +} +item { + name: "318761" + id: 2594 + display_name: "Sceloporus uniformis" +} +item { + name: "7068" + id: 2595 + display_name: "Tadorna ferruginea" +} +item { + name: "204077" + id: 2596 + display_name: "Achyra rantalis" +} +item { + name: "7470" + id: 2597 + display_name: "Campylorhynchus brunneicapillus" +} +item { + name: "32048" + id: 2598 + display_name: "Gerrhonotus infernalis" +} +item { + name: "204081" + id: 2599 + display_name: "Pyrausta laticlavia" +} +item { + name: "7476" + id: 2600 + display_name: "Campylorhynchus rufinucha" +} +item { + name: "32055" + id: 2601 + display_name: "Elgaria multicarinata" +} +item { + name: "244276" + id: 2602 + display_name: "Rhipidura fuliginosa" +} +item { + name: "144187" + id: 2603 + display_name: "Pyrisitia proterpia" +} +item { + name: "32059" + id: 2604 + display_name: "Elgaria multicarinata multicarinata" +} +item { + name: "32061" + id: 2605 + display_name: "Elgaria kingii" +} +item { + name: "146750" + id: 2606 + display_name: "Lascoria ambigualis" +} +item { + name: "32064" + id: 2607 + display_name: "Elgaria coerulea" +} +item { + name: "23873" + id: 2608 + display_name: "Hyla squirella" +} +item { + name: "48450" + id: 2609 + display_name: "Peltodoris nobilis" +} +item { + name: "64146" + id: 2610 + display_name: "Fissurella volcano" +} +item { + name: "48259" + id: 2611 + display_name: "Pelidnota punctata" +} +item { + name: "122185" + id: 2612 + display_name: "Pantherophis alleghaniensis quadrivittata" +} +item { + name: "7498" + id: 2613 + display_name: "Polioptila melanura" +} +item { + name: "56652" + id: 2614 + display_name: "Haliotis rufescens" +} +item { + name: "122191" + id: 2615 + display_name: "Pelecanus occidentalis carolinensis" +} +item { + name: "73041" + id: 2616 + display_name: "Melozone aberti" +} +item { + name: "199381" + id: 2617 + display_name: "Homalodisca vitripennis" +} +item { + name: "73044" + id: 2618 + display_name: "Melozone crissalis" +} +item { + name: "83290" + id: 2619 + display_name: "Zanclus cornutus" +} +item { + name: "7513" + id: 2620 + display_name: "Thryothorus ludovicianus" +} +item { + name: "28559" + id: 2621 + display_name: "Storeria occipitomaculata occipitomaculata" +} +item { + name: "24255" + id: 2622 + display_name: "Pseudacris maculata" +} +item { + name: "130398" + id: 2623 + display_name: "Melanargia galathea" +} +item { + name: "29925" + id: 2624 + display_name: "Heterodon platirhinos" +} +item { + name: "48484" + id: 2625 + display_name: "Harmonia axyridis" +} +item { + name: "122214" + id: 2626 + display_name: "Odontotaenius disjunctus" +} +item { + name: "39484" + id: 2627 + display_name: "Xantusia vigilis" +} +item { + name: "73919" + id: 2628 + display_name: "Podarcis sicula" +} +item { + name: "154553" + id: 2629 + display_name: "Leptoglossus clypealis" +} +item { + name: "23922" + id: 2630 + display_name: "Hyla intermedia" +} +item { + name: "122228" + id: 2631 + display_name: "Acharia stimulea" +} +item { + name: "108344" + id: 2632 + display_name: "Pantala flavescens" +} +item { + name: "118538" + id: 2633 + display_name: "Cotinis nitida" +} +item { + name: "23930" + id: 2634 + display_name: "Hyla chrysoscelis" +} +item { + name: "23933" + id: 2635 + display_name: "Hyla arenicolor" +} +item { + name: "122238" + id: 2636 + display_name: "Porcellio scaber" +} +item { + name: "479803" + id: 2637 + display_name: "Dioprosopa clavata" +} +item { + name: "5355" + id: 2638 + display_name: "Parabuteo unicinctus" +} +item { + name: "146822" + id: 2639 + display_name: "Texola elada" +} +item { + name: "236935" + id: 2640 + display_name: "Anas platyrhynchos domesticus" +} +item { + name: "7562" + id: 2641 + display_name: "Troglodytes aedon" +} +item { + name: "339444" + id: 2642 + display_name: "Buteo lineatus elegans" +} +item { + name: "42221" + id: 2643 + display_name: "Odocoileus hemionus columbianus" +} +item { + name: "15764" + id: 2644 + display_name: "Thamnophilus doliatus" +} +item { + name: "122261" + id: 2645 + display_name: "Cucullia convexipennis" +} +item { + name: "122262" + id: 2646 + display_name: "Brachystola magna" +} +item { + name: "7576" + id: 2647 + display_name: "Thryomanes bewickii" +} +item { + name: "143015" + id: 2648 + display_name: "Eubaphe mendica" +} +item { + name: "73592" + id: 2649 + display_name: "Actinemys marmorata" +} +item { + name: "84549" + id: 2650 + display_name: "Plathemis lydia" +} +item { + name: "23969" + id: 2651 + display_name: "Hyla cinerea" +} +item { + name: "318882" + id: 2652 + display_name: "Ancistrocerus gazella" +} +item { + name: "7072" + id: 2653 + display_name: "Tadorna variegata" +} +item { + name: "48548" + id: 2654 + display_name: "Vanessa cardui" +} +item { + name: "48549" + id: 2655 + display_name: "Vanessa virginiensis" +} +item { + name: "122278" + id: 2656 + display_name: "Pomacea canaliculata" +} +item { + name: "9457" + id: 2657 + display_name: "Myioborus miniatus" +} +item { + name: "122280" + id: 2658 + display_name: "Pyrgus albescens" +} +item { + name: "122281" + id: 2659 + display_name: "Calycopis cecrops" +} +item { + name: "130474" + id: 2660 + display_name: "Achlyodes pallida" +} +item { + name: "338503" + id: 2661 + display_name: "Phalacrocorax varius varius" +} +item { + name: "9458" + id: 2662 + display_name: "Myioborus pictus" +} +item { + name: "73629" + id: 2663 + display_name: "Anolis nebulosus" +} +item { + name: "122291" + id: 2664 + display_name: "Larus argentatus smithsonianus" +} +item { + name: "56756" + id: 2665 + display_name: "Murgantia histrionica" +} +item { + name: "73148" + id: 2666 + display_name: "Parkesia motacilla" +} +item { + name: "48575" + id: 2667 + display_name: "Okenia rosacea" +} +item { + name: "56768" + id: 2668 + display_name: "Sula granti" +} +item { + name: "48578" + id: 2669 + display_name: "Anteos maerula" +} +item { + name: "64968" + id: 2670 + display_name: "Anaxyrus americanus" +} +item { + name: "64970" + id: 2671 + display_name: "Anaxyrus boreas" +} +item { + name: "115549" + id: 2672 + display_name: "Crotalus lepidus lepidus" +} +item { + name: "64977" + id: 2673 + display_name: "Anaxyrus fowleri" +} +item { + name: "19022" + id: 2674 + display_name: "Ara macao" +} +item { + name: "24259" + id: 2675 + display_name: "Pseudacris regilla" +} +item { + name: "64984" + id: 2676 + display_name: "Anaxyrus punctatus" +} +item { + name: "64985" + id: 2677 + display_name: "Anaxyrus quercicus" +} +item { + name: "73178" + id: 2678 + display_name: "Peucaea ruficauda" +} +item { + name: "64987" + id: 2679 + display_name: "Anaxyrus speciosus" +} +item { + name: "64989" + id: 2680 + display_name: "Anaxyrus woodhousii" +} +item { + name: "339596" + id: 2681 + display_name: "Calidris subruficollis" +} +item { + name: "56552" + id: 2682 + display_name: "Carabus nemoralis" +} +item { + name: "84722" + id: 2683 + display_name: "Ischnura verticalis" +} +item { + name: "122356" + id: 2684 + display_name: "Eumorpha achemon" +} +item { + name: "318965" + id: 2685 + display_name: "Chrysolina bankii" +} +item { + name: "228855" + id: 2686 + display_name: "Protodeltote muscosula" +} +item { + name: "146940" + id: 2687 + display_name: "Agriphila vulgivagella" +} +item { + name: "56832" + id: 2688 + display_name: "Nymphalis antiopa" +} +item { + name: "61355" + id: 2689 + display_name: "Vespula pensylvanica" +} +item { + name: "48645" + id: 2690 + display_name: "Megathura crenulata" +} +item { + name: "73222" + id: 2691 + display_name: "Phoenicopterus roseus" +} +item { + name: "363354" + id: 2692 + display_name: "Lobatus gigas" +} +item { + name: "3802" + id: 2693 + display_name: "Morus bassanus" +} +item { + name: "62722" + id: 2694 + display_name: "Apalone spinifera spinifera" +} +item { + name: "48655" + id: 2695 + display_name: "Aplysia californica" +} +item { + name: "54468" + id: 2696 + display_name: "Aglais urticae" +} +item { + name: "48662" + id: 2697 + display_name: "Danaus plexippus" +} +item { + name: "49071" + id: 2698 + display_name: "Metridium senile" +} +item { + name: "228899" + id: 2699 + display_name: "Psamatodes abydata" +} +item { + name: "133102" + id: 2700 + display_name: "Oncometopia orbona" +} +item { + name: "39659" + id: 2701 + display_name: "Chelonia mydas" +} +item { + name: "121437" + id: 2702 + display_name: "Dolomedes triton" +} +item { + name: "94545" + id: 2703 + display_name: "Argia fumipennis" +} +item { + name: "56887" + id: 2704 + display_name: "Bombus pensylvanicus" +} +item { + name: "40509" + id: 2705 + display_name: "Eptesicus fuscus" +} +item { + name: "58635" + id: 2706 + display_name: "Lepomis megalotis" +} +item { + name: "100369" + id: 2707 + display_name: "Erpetogomphus designatus" +} +item { + name: "58636" + id: 2708 + display_name: "Lepomis cyanellus" +} +item { + name: "40522" + id: 2709 + display_name: "Lasiurus borealis" +} +item { + name: "102006" + id: 2710 + display_name: "Hagenius brevistylus" +} +item { + name: "50283" + id: 2711 + display_name: "Marpesia petreus" +} +item { + name: "123829" + id: 2712 + display_name: "Pelecanus occidentalis californicus" +} +item { + name: "62453" + id: 2713 + display_name: "Anthidium manicatum" +} +item { + name: "56925" + id: 2714 + display_name: "Graphocephala coccinea" +} +item { + name: "48738" + id: 2715 + display_name: "Sphex pensylvanicus" +} +item { + name: "43151" + id: 2716 + display_name: "Oryctolagus cuniculus" +} +item { + name: "19822" + id: 2717 + display_name: "Glaucidium brasilianum" +} +item { + name: "48750" + id: 2718 + display_name: "Lottia scabra" +} +item { + name: "335071" + id: 2719 + display_name: "Elophila obliteralis" +} +item { + name: "81521" + id: 2720 + display_name: "Vipera berus" +} +item { + name: "43697" + id: 2721 + display_name: "Elephas maximus" +} +item { + name: "7079" + id: 2722 + display_name: "Oxyura jamaicensis" +} +item { + name: "43042" + id: 2723 + display_name: "Erinaceus europaeus" +} +item { + name: "40086" + id: 2724 + display_name: "Gopherus agassizii" +} +item { + name: "81545" + id: 2725 + display_name: "Lumbricus terrestris" +} +item { + name: "16010" + id: 2726 + display_name: "Myiarchus cinerascens" +} +item { + name: "2669" + id: 2727 + display_name: "Chloroceryle americana" +} +item { + name: "9535" + id: 2728 + display_name: "Sturnella neglecta" +} +item { + name: "81554" + id: 2729 + display_name: "Ictalurus punctatus" +} +item { + name: "339907" + id: 2730 + display_name: "Ramphastos ambiguus" +} +item { + name: "39814" + id: 2731 + display_name: "Terrapene carolina" +} +item { + name: "10254" + id: 2732 + display_name: "Paroaria coronata" +} +item { + name: "40614" + id: 2733 + display_name: "Antrozous pallidus" +} +item { + name: "502385" + id: 2734 + display_name: "Probole amicaria" +} +item { + name: "24233" + id: 2735 + display_name: "Acris gryllus" +} +item { + name: "81579" + id: 2736 + display_name: "Steatoda triangulosa" +} +item { + name: "81580" + id: 2737 + display_name: "Callosamia promethea" +} +item { + name: "146034" + id: 2738 + display_name: "Coluber lateralis" +} +item { + name: "81582" + id: 2739 + display_name: "Hyalophora cecropia" +} +item { + name: "81583" + id: 2740 + display_name: "Anisota senatoria" +} +item { + name: "66002" + id: 2741 + display_name: "Lithobates palustris" +} +item { + name: "81586" + id: 2742 + display_name: "Citheronia regalis" +} +item { + name: "40629" + id: 2743 + display_name: "Lasionycteris noctivagans" +} +item { + name: "81590" + id: 2744 + display_name: "Eacles imperialis" +} +item { + name: "204472" + id: 2745 + display_name: "Buteo buteo" +} +item { + name: "65212" + id: 2746 + display_name: "Craugastor augusti" +} +item { + name: "48830" + id: 2747 + display_name: "Patiria miniata" +} +item { + name: "48833" + id: 2748 + display_name: "Pisaster giganteus" +} +item { + name: "16071" + id: 2749 + display_name: "Myiodynastes luteiventris" +} +item { + name: "81610" + id: 2750 + display_name: "Balanus glandula" +} +item { + name: "24268" + id: 2751 + display_name: "Pseudacris crucifer" +} +item { + name: "16079" + id: 2752 + display_name: "Contopus sordidulus" +} +item { + name: "204496" + id: 2753 + display_name: "Corvus corone" +} +item { + name: "204498" + id: 2754 + display_name: "Cyanoramphus novaezelandiae" +} +item { + name: "24277" + id: 2755 + display_name: "Smilisca baudinii" +} +item { + name: "22631" + id: 2756 + display_name: "Eleutherodactylus planirostris" +} +item { + name: "16100" + id: 2757 + display_name: "Contopus virens" +} +item { + name: "42278" + id: 2758 + display_name: "Aepyceros melampus" +} +item { + name: "16106" + id: 2759 + display_name: "Contopus pertinax" +} +item { + name: "16110" + id: 2760 + display_name: "Contopus cooperi" +} +item { + name: "42280" + id: 2761 + display_name: "Connochaetes taurinus" +} +item { + name: "47455" + id: 2762 + display_name: "Octopus rubescens" +} +item { + name: "204533" + id: 2763 + display_name: "Larus argentatus" +} +item { + name: "81656" + id: 2764 + display_name: "Nematocampa resistaria" +} +item { + name: "81657" + id: 2765 + display_name: "Lacinipolia renigera" +} +item { + name: "204519" + id: 2766 + display_name: "Halcyon smyrnensis" +} +item { + name: "62762" + id: 2767 + display_name: "Cordulegaster dorsalis" +} +item { + name: "81663" + id: 2768 + display_name: "Malacosoma disstria" +} +item { + name: "32512" + id: 2769 + display_name: "Rena dulcis" +} +item { + name: "81665" + id: 2770 + display_name: "Orgyia leucostigma" +} +item { + name: "130821" + id: 2771 + display_name: "Haploa confusa" +} +item { + name: "81672" + id: 2772 + display_name: "Clemensia albata" +} +item { + name: "204554" + id: 2773 + display_name: "Onychognathus morio" +} +item { + name: "81677" + id: 2774 + display_name: "Euchaetes egle" +} +item { + name: "81680" + id: 2775 + display_name: "Scopula limboundata" +} +item { + name: "318497" + id: 2776 + display_name: "Hemipenthes sinuosa" +} +item { + name: "179987" + id: 2777 + display_name: "Ictidomys parvidens" +} +item { + name: "179988" + id: 2778 + display_name: "Ictidomys tridecemlineatus" +} +item { + name: "81685" + id: 2779 + display_name: "Evergestis pallidata" +} +item { + name: "81687" + id: 2780 + display_name: "Noctua pronuba" +} +item { + name: "179992" + id: 2781 + display_name: "Xerospermophilus spilosoma" +} +item { + name: "179994" + id: 2782 + display_name: "Urocitellus armatus" +} +item { + name: "9519" + id: 2783 + display_name: "Cyanocompsa parellina" +} +item { + name: "179998" + id: 2784 + display_name: "Urocitellus columbianus" +} +item { + name: "114463" + id: 2785 + display_name: "Trithemis annulata" +} +item { + name: "199169" + id: 2786 + display_name: "Catocala maestosa" +} +item { + name: "143323" + id: 2787 + display_name: "Tolype velleda" +} +item { + name: "120113" + id: 2788 + display_name: "Anthrenus verbasci" +} +item { + name: "7601" + id: 2789 + display_name: "Cistothorus palustris" +} +item { + name: "81706" + id: 2790 + display_name: "Alaus oculatus" +} +item { + name: "220974" + id: 2791 + display_name: "Harrisimemna trisignata" +} +item { + name: "20445" + id: 2792 + display_name: "Tyto alba" +} +item { + name: "73523" + id: 2793 + display_name: "Trogon caligatus" +} +item { + name: "49590" + id: 2794 + display_name: "Micropterus dolomieu" +} +item { + name: "41729" + id: 2795 + display_name: "Mirounga leonina" +} +item { + name: "48957" + id: 2796 + display_name: "Arilus cristatus" +} +item { + name: "81727" + id: 2797 + display_name: "Abaeis nicippe" +} +item { + name: "8000" + id: 2798 + display_name: "Corvus monedula" +} +item { + name: "8001" + id: 2799 + display_name: "Corvus ossifragus" +} +item { + name: "171843" + id: 2800 + display_name: "Rabdotus dealbatus" +} +item { + name: "81734" + id: 2801 + display_name: "Neophasia menapia" +} +item { + name: "258813" + id: 2802 + display_name: "Clogmia albipunctata" +} +item { + name: "332243" + id: 2803 + display_name: "Lepturobosca chrysocoma" +} +item { + name: "81744" + id: 2804 + display_name: "Heliconius erato" +} +item { + name: "218424" + id: 2805 + display_name: "Dicymolomia julianalis" +} +item { + name: "3813" + id: 2806 + display_name: "Spheniscus demersus" +} +item { + name: "81749" + id: 2807 + display_name: "Malacosoma americanum" +} +item { + name: "81752" + id: 2808 + display_name: "Pyrausta tyralis" +} +item { + name: "48987" + id: 2809 + display_name: "Hippodamia convergens" +} +item { + name: "8029" + id: 2810 + display_name: "Corvus frugilegus" +} +item { + name: "8031" + id: 2811 + display_name: "Corvus splendens" +} +item { + name: "147298" + id: 2812 + display_name: "Lasiommata megera" +} +item { + name: "7087" + id: 2813 + display_name: "Branta bernicla" +} +item { + name: "48550" + id: 2814 + display_name: "Phoebis sennae" +} +item { + name: "4349" + id: 2815 + display_name: "Larus hyperboreus" +} +item { + name: "84027" + id: 2816 + display_name: "Trigonopeltastes delta" +} +item { + name: "194762" + id: 2817 + display_name: "Vanessa itea" +} +item { + name: "311163" + id: 2818 + display_name: "Pseudomops septentrionalis" +} +item { + name: "55957" + id: 2819 + display_name: "Scudderia furcata" +} +item { + name: "39822" + id: 2820 + display_name: "Pseudemys texana" +} +item { + name: "204685" + id: 2821 + display_name: "Chlosyne ehrenbergii" +} +item { + name: "122767" + id: 2822 + display_name: "Columba livia domestica" +} +item { + name: "55960" + id: 2823 + display_name: "Sceloporus graciosus" +} +item { + name: "121823" + id: 2824 + display_name: "Autographa californica" +} +item { + name: "8088" + id: 2825 + display_name: "Garrulus glandarius" +} +item { + name: "65433" + id: 2826 + display_name: "Ecnomiohyla miotympanum" +} +item { + name: "49051" + id: 2827 + display_name: "Anthopleura sola" +} +item { + name: "125815" + id: 2828 + display_name: "Coenonympha arcania" +} +item { + name: "55963" + id: 2829 + display_name: "Malacosoma californicum" +} +item { + name: "120479" + id: 2830 + display_name: "Anser anser domesticus" +} +item { + name: "133788" + id: 2831 + display_name: "Xylocopa micans" +} +item { + name: "81559" + id: 2832 + display_name: "Epargyreus clarus" +} +item { + name: "81839" + id: 2833 + display_name: "Platycryptus undatus" +} +item { + name: "133791" + id: 2834 + display_name: "Polistes exclamans" +} +item { + name: "84640" + id: 2835 + display_name: "Polistes dominula" +} +item { + name: "73666" + id: 2836 + display_name: "Aspidoscelis exsanguis" +} +item { + name: "73669" + id: 2837 + display_name: "Aspidoscelis gularis" +} +item { + name: "16326" + id: 2838 + display_name: "Mitrephanes phaeocercus" +} +item { + name: "49095" + id: 2839 + display_name: "Pagurus samuelis" +} +item { + name: "73672" + id: 2840 + display_name: "Aspidoscelis hyperythra" +} +item { + name: "59192" + id: 2841 + display_name: "Polites sabuleti" +} +item { + name: "81561" + id: 2842 + display_name: "Anaea andria" +} +item { + name: "81881" + id: 2843 + display_name: "Amphipsalta zelandica" +} +item { + name: "73690" + id: 2844 + display_name: "Aspidoscelis sexlineata" +} +item { + name: "73694" + id: 2845 + display_name: "Aspidoscelis velox" +} +item { + name: "335840" + id: 2846 + display_name: "Pyrausta inornatalis" +} +item { + name: "49126" + id: 2847 + display_name: "Strongylocentrotus franciscanus" +} +item { + name: "204775" + id: 2848 + display_name: "Kricogonia lyside" +} +item { + name: "475115" + id: 2849 + display_name: "Ardenna creatopus" +} +item { + name: "475120" + id: 2850 + display_name: "Ardenna gravis" +} +item { + name: "62803" + id: 2851 + display_name: "Monadenia fidelis" +} +item { + name: "49150" + id: 2852 + display_name: "Agraulis vanillae" +} +item { + name: "83929" + id: 2853 + display_name: "Phanaeus vindex" +} +item { + name: "199839" + id: 2854 + display_name: "Haemorhous cassinii" +} diff --git a/workspace/virtuallab/object_detection/data/kitti_label_map.pbtxt b/workspace/virtuallab/object_detection/data/kitti_label_map.pbtxt new file mode 100644 index 0000000..0afcc69 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/kitti_label_map.pbtxt @@ -0,0 +1,9 @@ +item { + id: 1 + name: 'car' +} + +item { + id: 2 + name: 'pedestrian' +} diff --git a/workspace/virtuallab/object_detection/data/mscoco_complete_label_map.pbtxt b/workspace/virtuallab/object_detection/data/mscoco_complete_label_map.pbtxt new file mode 100644 index 0000000..d73fc06 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/mscoco_complete_label_map.pbtxt @@ -0,0 +1,455 @@ +item { + name: "background" + id: 0 + display_name: "background" +} +item { + name: "/m/01g317" + id: 1 + display_name: "person" +} +item { + name: "/m/0199g" + id: 2 + display_name: "bicycle" +} +item { + name: "/m/0k4j" + id: 3 + display_name: "car" +} +item { + name: "/m/04_sv" + id: 4 + display_name: "motorcycle" +} +item { + name: "/m/05czz6l" + id: 5 + display_name: "airplane" +} +item { + name: "/m/01bjv" + id: 6 + display_name: "bus" +} +item { + name: "/m/07jdr" + id: 7 + display_name: "train" +} +item { + name: "/m/07r04" + id: 8 + display_name: "truck" +} +item { + name: "/m/019jd" + id: 9 + display_name: "boat" +} +item { + name: "/m/015qff" + id: 10 + display_name: "traffic light" +} +item { + name: "/m/01pns0" + id: 11 + display_name: "fire hydrant" +} +item { + name: "12" + id: 12 + display_name: "12" +} +item { + name: "/m/02pv19" + id: 13 + display_name: "stop sign" +} +item { + name: "/m/015qbp" + id: 14 + display_name: "parking meter" +} +item { + name: "/m/0cvnqh" + id: 15 + display_name: "bench" +} +item { + name: "/m/015p6" + id: 16 + display_name: "bird" +} +item { + name: "/m/01yrx" + id: 17 + display_name: "cat" +} +item { + name: "/m/0bt9lr" + id: 18 + display_name: "dog" +} +item { + name: "/m/03k3r" + id: 19 + display_name: "horse" +} +item { + name: "/m/07bgp" + id: 20 + display_name: "sheep" +} +item { + name: "/m/01xq0k1" + id: 21 + display_name: "cow" +} +item { + name: "/m/0bwd_0j" + id: 22 + display_name: "elephant" +} +item { + name: "/m/01dws" + id: 23 + display_name: "bear" +} +item { + name: "/m/0898b" + id: 24 + display_name: "zebra" +} +item { + name: "/m/03bk1" + id: 25 + display_name: "giraffe" +} +item { + name: "26" + id: 26 + display_name: "26" +} +item { + name: "/m/01940j" + id: 27 + display_name: "backpack" +} +item { + name: "/m/0hnnb" + id: 28 + display_name: "umbrella" +} +item { + name: "29" + id: 29 + display_name: "29" +} +item { + name: "30" + id: 30 + display_name: "30" +} +item { + name: "/m/080hkjn" + id: 31 + display_name: "handbag" +} +item { + name: "/m/01rkbr" + id: 32 + display_name: "tie" +} +item { + name: "/m/01s55n" + id: 33 + display_name: "suitcase" +} +item { + name: "/m/02wmf" + id: 34 + display_name: "frisbee" +} +item { + name: "/m/071p9" + id: 35 + display_name: "skis" +} +item { + name: "/m/06__v" + id: 36 + display_name: "snowboard" +} +item { + name: "/m/018xm" + id: 37 + display_name: "sports ball" +} +item { + name: "/m/02zt3" + id: 38 + display_name: "kite" +} +item { + name: "/m/03g8mr" + id: 39 + display_name: "baseball bat" +} +item { + name: "/m/03grzl" + id: 40 + display_name: "baseball glove" +} +item { + name: "/m/06_fw" + id: 41 + display_name: "skateboard" +} +item { + name: "/m/019w40" + id: 42 + display_name: "surfboard" +} +item { + name: "/m/0dv9c" + id: 43 + display_name: "tennis racket" +} +item { + name: "/m/04dr76w" + id: 44 + display_name: "bottle" +} +item { + name: "45" + id: 45 + display_name: "45" +} +item { + name: "/m/09tvcd" + id: 46 + display_name: "wine glass" +} +item { + name: "/m/08gqpm" + id: 47 + display_name: "cup" +} +item { + name: "/m/0dt3t" + id: 48 + display_name: "fork" +} +item { + name: "/m/04ctx" + id: 49 + display_name: "knife" +} +item { + name: "/m/0cmx8" + id: 50 + display_name: "spoon" +} +item { + name: "/m/04kkgm" + id: 51 + display_name: "bowl" +} +item { + name: "/m/09qck" + id: 52 + display_name: "banana" +} +item { + name: "/m/014j1m" + id: 53 + display_name: "apple" +} +item { + name: "/m/0l515" + id: 54 + display_name: "sandwich" +} +item { + name: "/m/0cyhj_" + id: 55 + display_name: "orange" +} +item { + name: "/m/0hkxq" + id: 56 + display_name: "broccoli" +} +item { + name: "/m/0fj52s" + id: 57 + display_name: "carrot" +} +item { + name: "/m/01b9xk" + id: 58 + display_name: "hot dog" +} +item { + name: "/m/0663v" + id: 59 + display_name: "pizza" +} +item { + name: "/m/0jy4k" + id: 60 + display_name: "donut" +} +item { + name: "/m/0fszt" + id: 61 + display_name: "cake" +} +item { + name: "/m/01mzpv" + id: 62 + display_name: "chair" +} +item { + name: "/m/02crq1" + id: 63 + display_name: "couch" +} +item { + name: "/m/03fp41" + id: 64 + display_name: "potted plant" +} +item { + name: "/m/03ssj5" + id: 65 + display_name: "bed" +} +item { + name: "66" + id: 66 + display_name: "66" +} +item { + name: "/m/04bcr3" + id: 67 + display_name: "dining table" +} +item { + name: "68" + id: 68 + display_name: "68" +} +item { + name: "69" + id: 69 + display_name: "69" +} +item { + name: "/m/09g1w" + id: 70 + display_name: "toilet" +} +item { + name: "71" + id: 71 + display_name: "71" +} +item { + name: "/m/07c52" + id: 72 + display_name: "tv" +} +item { + name: "/m/01c648" + id: 73 + display_name: "laptop" +} +item { + name: "/m/020lf" + id: 74 + display_name: "mouse" +} +item { + name: "/m/0qjjc" + id: 75 + display_name: "remote" +} +item { + name: "/m/01m2v" + id: 76 + display_name: "keyboard" +} +item { + name: "/m/050k8" + id: 77 + display_name: "cell phone" +} +item { + name: "/m/0fx9l" + id: 78 + display_name: "microwave" +} +item { + name: "/m/029bxz" + id: 79 + display_name: "oven" +} +item { + name: "/m/01k6s3" + id: 80 + display_name: "toaster" +} +item { + name: "/m/0130jx" + id: 81 + display_name: "sink" +} +item { + name: "/m/040b_t" + id: 82 + display_name: "refrigerator" +} +item { + name: "83" + id: 83 + display_name: "83" +} +item { + name: "/m/0bt_c3" + id: 84 + display_name: "book" +} +item { + name: "/m/01x3z" + id: 85 + display_name: "clock" +} +item { + name: "/m/02s195" + id: 86 + display_name: "vase" +} +item { + name: "/m/01lsmm" + id: 87 + display_name: "scissors" +} +item { + name: "/m/0kmg4" + id: 88 + display_name: "teddy bear" +} +item { + name: "/m/03wvsk" + id: 89 + display_name: "hair drier" +} +item { + name: "/m/012xff" + id: 90 + display_name: "toothbrush" +} diff --git a/workspace/virtuallab/object_detection/data/mscoco_label_map.pbtxt b/workspace/virtuallab/object_detection/data/mscoco_label_map.pbtxt new file mode 100644 index 0000000..1f4872b --- /dev/null +++ b/workspace/virtuallab/object_detection/data/mscoco_label_map.pbtxt @@ -0,0 +1,400 @@ +item { + name: "/m/01g317" + id: 1 + display_name: "person" +} +item { + name: "/m/0199g" + id: 2 + display_name: "bicycle" +} +item { + name: "/m/0k4j" + id: 3 + display_name: "car" +} +item { + name: "/m/04_sv" + id: 4 + display_name: "motorcycle" +} +item { + name: "/m/05czz6l" + id: 5 + display_name: "airplane" +} +item { + name: "/m/01bjv" + id: 6 + display_name: "bus" +} +item { + name: "/m/07jdr" + id: 7 + display_name: "train" +} +item { + name: "/m/07r04" + id: 8 + display_name: "truck" +} +item { + name: "/m/019jd" + id: 9 + display_name: "boat" +} +item { + name: "/m/015qff" + id: 10 + display_name: "traffic light" +} +item { + name: "/m/01pns0" + id: 11 + display_name: "fire hydrant" +} +item { + name: "/m/02pv19" + id: 13 + display_name: "stop sign" +} +item { + name: "/m/015qbp" + id: 14 + display_name: "parking meter" +} +item { + name: "/m/0cvnqh" + id: 15 + display_name: "bench" +} +item { + name: "/m/015p6" + id: 16 + display_name: "bird" +} +item { + name: "/m/01yrx" + id: 17 + display_name: "cat" +} +item { + name: "/m/0bt9lr" + id: 18 + display_name: "dog" +} +item { + name: "/m/03k3r" + id: 19 + display_name: "horse" +} +item { + name: "/m/07bgp" + id: 20 + display_name: "sheep" +} +item { + name: "/m/01xq0k1" + id: 21 + display_name: "cow" +} +item { + name: "/m/0bwd_0j" + id: 22 + display_name: "elephant" +} +item { + name: "/m/01dws" + id: 23 + display_name: "bear" +} +item { + name: "/m/0898b" + id: 24 + display_name: "zebra" +} +item { + name: "/m/03bk1" + id: 25 + display_name: "giraffe" +} +item { + name: "/m/01940j" + id: 27 + display_name: "backpack" +} +item { + name: "/m/0hnnb" + id: 28 + display_name: "umbrella" +} +item { + name: "/m/080hkjn" + id: 31 + display_name: "handbag" +} +item { + name: "/m/01rkbr" + id: 32 + display_name: "tie" +} +item { + name: "/m/01s55n" + id: 33 + display_name: "suitcase" +} +item { + name: "/m/02wmf" + id: 34 + display_name: "frisbee" +} +item { + name: "/m/071p9" + id: 35 + display_name: "skis" +} +item { + name: "/m/06__v" + id: 36 + display_name: "snowboard" +} +item { + name: "/m/018xm" + id: 37 + display_name: "sports ball" +} +item { + name: "/m/02zt3" + id: 38 + display_name: "kite" +} +item { + name: "/m/03g8mr" + id: 39 + display_name: "baseball bat" +} +item { + name: "/m/03grzl" + id: 40 + display_name: "baseball glove" +} +item { + name: "/m/06_fw" + id: 41 + display_name: "skateboard" +} +item { + name: "/m/019w40" + id: 42 + display_name: "surfboard" +} +item { + name: "/m/0dv9c" + id: 43 + display_name: "tennis racket" +} +item { + name: "/m/04dr76w" + id: 44 + display_name: "bottle" +} +item { + name: "/m/09tvcd" + id: 46 + display_name: "wine glass" +} +item { + name: "/m/08gqpm" + id: 47 + display_name: "cup" +} +item { + name: "/m/0dt3t" + id: 48 + display_name: "fork" +} +item { + name: "/m/04ctx" + id: 49 + display_name: "knife" +} +item { + name: "/m/0cmx8" + id: 50 + display_name: "spoon" +} +item { + name: "/m/04kkgm" + id: 51 + display_name: "bowl" +} +item { + name: "/m/09qck" + id: 52 + display_name: "banana" +} +item { + name: "/m/014j1m" + id: 53 + display_name: "apple" +} +item { + name: "/m/0l515" + id: 54 + display_name: "sandwich" +} +item { + name: "/m/0cyhj_" + id: 55 + display_name: "orange" +} +item { + name: "/m/0hkxq" + id: 56 + display_name: "broccoli" +} +item { + name: "/m/0fj52s" + id: 57 + display_name: "carrot" +} +item { + name: "/m/01b9xk" + id: 58 + display_name: "hot dog" +} +item { + name: "/m/0663v" + id: 59 + display_name: "pizza" +} +item { + name: "/m/0jy4k" + id: 60 + display_name: "donut" +} +item { + name: "/m/0fszt" + id: 61 + display_name: "cake" +} +item { + name: "/m/01mzpv" + id: 62 + display_name: "chair" +} +item { + name: "/m/02crq1" + id: 63 + display_name: "couch" +} +item { + name: "/m/03fp41" + id: 64 + display_name: "potted plant" +} +item { + name: "/m/03ssj5" + id: 65 + display_name: "bed" +} +item { + name: "/m/04bcr3" + id: 67 + display_name: "dining table" +} +item { + name: "/m/09g1w" + id: 70 + display_name: "toilet" +} +item { + name: "/m/07c52" + id: 72 + display_name: "tv" +} +item { + name: "/m/01c648" + id: 73 + display_name: "laptop" +} +item { + name: "/m/020lf" + id: 74 + display_name: "mouse" +} +item { + name: "/m/0qjjc" + id: 75 + display_name: "remote" +} +item { + name: "/m/01m2v" + id: 76 + display_name: "keyboard" +} +item { + name: "/m/050k8" + id: 77 + display_name: "cell phone" +} +item { + name: "/m/0fx9l" + id: 78 + display_name: "microwave" +} +item { + name: "/m/029bxz" + id: 79 + display_name: "oven" +} +item { + name: "/m/01k6s3" + id: 80 + display_name: "toaster" +} +item { + name: "/m/0130jx" + id: 81 + display_name: "sink" +} +item { + name: "/m/040b_t" + id: 82 + display_name: "refrigerator" +} +item { + name: "/m/0bt_c3" + id: 84 + display_name: "book" +} +item { + name: "/m/01x3z" + id: 85 + display_name: "clock" +} +item { + name: "/m/02s195" + id: 86 + display_name: "vase" +} +item { + name: "/m/01lsmm" + id: 87 + display_name: "scissors" +} +item { + name: "/m/0kmg4" + id: 88 + display_name: "teddy bear" +} +item { + name: "/m/03wvsk" + id: 89 + display_name: "hair drier" +} +item { + name: "/m/012xff" + id: 90 + display_name: "toothbrush" +} diff --git a/workspace/virtuallab/object_detection/data/mscoco_minival_ids.txt b/workspace/virtuallab/object_detection/data/mscoco_minival_ids.txt new file mode 100644 index 0000000..5bbff3c --- /dev/null +++ b/workspace/virtuallab/object_detection/data/mscoco_minival_ids.txt @@ -0,0 +1,8059 @@ +25096 +251824 +35313 +546011 +524186 +205866 +511403 +313916 +47471 +258628 +233560 +576017 +404517 +410056 +178690 +248980 +511724 +429718 +163076 +244111 +126766 +313182 +191981 +139992 +325237 +248129 +214519 +175438 +493321 +174103 +563762 +536795 +289960 +473720 +515540 +292118 +360851 +267175 +532876 +171613 +581415 +259819 +441841 +381682 +58157 +4980 +473929 +70626 +93773 +283412 +36765 +495020 +278401 +329307 +192810 +491784 +506416 +225495 +553747 +86442 +242208 +132686 +385877 +290248 +525705 +5476 +486521 +332512 +138556 +348083 +284375 +40018 +296994 +38685 +432429 +183407 +434358 +472164 +530494 +570693 +193401 +392612 +98872 +445766 +532209 +98322 +285114 +267725 +51605 +314812 +91105 +535506 +540264 +375341 +449828 +277659 +68933 +76873 +217554 +213592 +190776 +516224 +474479 +343599 +578813 +128669 +546292 +475365 +377626 +128833 +427091 +547227 +11742 +80213 +462241 +374574 +121572 +29151 +13892 +262394 +303667 +198724 +7320 +448492 +419080 +460379 +483965 +556516 +139181 +1103 +308715 +207507 +213827 +216083 +445597 +240275 +379585 +116389 +138124 +559051 +326898 +419386 +503660 +519460 +23893 +24458 +518109 +462982 +151492 +514254 +2477 +147165 +570394 +548766 +250083 +364341 +351967 +386277 +328084 +511299 +499349 +315501 +234965 +428562 +219771 +288150 +136021 +168619 +298316 +75118 +189752 +243857 +296222 +554002 +533628 +384596 +202981 +498350 +391463 +183991 +528062 +451084 +7899 +408534 +329030 +318566 +22492 +361285 +226973 +213356 +417265 +105622 +161169 +261487 +167477 +233370 +142999 +256713 +305833 +103579 +352538 +135763 +392144 +61181 +200302 +456908 +286858 +179850 +488075 +174511 +194755 +317822 +2302 +304596 +172556 +548275 +341678 +55299 +134760 +352936 +545129 +377012 +141328 +103757 +552837 +28246 +125167 +328745 +278760 +337133 +403389 +146825 +502558 +265916 +428985 +492041 +113403 +372037 +306103 +287574 +187495 +479805 +336309 +162043 +95899 +43133 +464248 +149115 +247438 +74030 +130645 +282841 +127092 +101172 +536743 +179642 +58133 +49667 +170605 +11347 +365277 +201970 +292663 +217219 +463226 +41924 +281102 +357816 +490878 +100343 +525058 +133503 +416145 +29341 +415413 +125527 +507951 +262609 +240210 +581781 +345137 +526342 +268641 +328777 +32001 +137538 +39115 +415958 +6771 +421865 +64909 +383601 +206907 +420840 +370980 +28452 +571893 +153520 +185890 +392991 +547013 +257359 +279879 +478614 +131919 +40937 +22874 +173375 +106344 +44801 +205401 +312870 +400886 +351530 +344013 +173500 +470423 +396729 +402499 +276585 +377097 +367619 +518908 +263866 +332292 +67805 +152211 +515025 +221350 +525247 +78490 +504342 +95908 +82668 +256199 +220270 +552065 +242379 +84866 +152281 +228464 +223122 +67537 +456968 +368349 +101985 +14681 +543551 +107558 +372009 +99054 +126540 +86877 +492785 +482585 +571564 +501116 +296871 +20395 +181518 +568041 +121154 +56187 +190018 +97156 +310325 +393274 +214574 +243222 +289949 +452121 +150508 +341752 +310757 +24040 +228551 +335589 +12020 +529597 +459884 +344888 +229713 +51948 +370929 +552061 +261072 +120070 +332067 +263014 +158993 +451714 +397327 +20965 +414340 +574946 +370266 +487534 +492246 +264771 +73702 +43997 +235124 +301093 +400048 +77681 +58472 +331386 +13783 +242513 +419158 +59325 +383033 +393258 +529041 +249276 +182775 +351793 +9727 +334069 +566771 +539355 +38662 +423617 +47559 +120592 +508303 +462565 +47916 +218208 +182362 +562101 +441442 +71239 +395378 +522637 +25603 +484450 +872 +171483 +527248 +323155 +240754 +15032 +419144 +313214 +250917 +333430 +242757 +221914 +283190 +194297 +228506 +550691 +172513 +312192 +530619 +113867 +323552 +374115 +35435 +160239 +62877 +441873 +196574 +62858 +557114 +427612 +242869 +356733 +304828 +24880 +490509 +407083 +457877 +402788 +536416 +385912 +544121 +500389 +451102 +12120 +483476 +70987 +482799 +542549 +49236 +424258 +435783 +182366 +438093 +501824 +232845 +53965 +223198 +288933 +450458 +285664 +196484 +408930 +519815 +290981 +398567 +315792 +490683 +257136 +75611 +302498 +332153 +82293 +416911 +558608 +564659 +536195 +370260 +57904 +527270 +6593 +145620 +551650 +470832 +515785 +251404 +287331 +150788 +334006 +266117 +10039 +579158 +328397 +468351 +550400 +31745 +405970 +16761 +323515 +459598 +558457 +570736 +476939 +472610 +72155 +112517 +13659 +530905 +458768 +43486 +560893 +493174 +31217 +262736 +412204 +142722 +151231 +480643 +197245 +398666 +444869 +110999 +191724 +479057 +492420 +170638 +277329 +301908 +395644 +537611 +141887 +47149 +403432 +34818 +372495 +67994 +337497 +478586 +249815 +533462 +281032 +289941 +151911 +271215 +407868 +360700 +508582 +103873 +353658 +369081 +406403 +331692 +26430 +105655 +572630 +37181 +91336 +484587 +318284 +113019 +33055 +25293 +229324 +374052 +384111 +213951 +315195 +319283 +539453 +17655 +308974 +326243 +539436 +417876 +526940 +356347 +221932 +73753 +292648 +262284 +304924 +558587 +374858 +253518 +311744 +539636 +40924 +136624 +334305 +365997 +63355 +191226 +526732 +367128 +575198 +500657 +50637 +17182 +424792 +565353 +563040 +383494 +74458 +155142 +197125 +223857 +428241 +440830 +371289 +437303 +330449 +93771 +82715 +499631 +381257 +563951 +192834 +528600 +404273 +270554 +208053 +188613 +484760 +432016 +129800 +91756 +523097 +317018 +487282 +444913 +159500 +126822 +540564 +105812 +560756 +306099 +471226 +123842 +513219 +154877 +497034 +283928 +564003 +238602 +194780 +462728 +558640 +524373 +455624 +3690 +560367 +316351 +455772 +223777 +161517 +243034 +250440 +239975 +441008 +324715 +152106 +246973 +462805 +296521 +412767 +530913 +370165 +292526 +107244 +217440 +330204 +220176 +577735 +197022 +127451 +518701 +212322 +204887 +27696 +348474 +119233 +282804 +230040 +425690 +409241 +296825 +296353 +375909 +123136 +573891 +338256 +198247 +373375 +151051 +500084 +557596 +120478 +44989 +283380 +149005 +522065 +626 +17198 +309633 +524245 +291589 +322714 +455847 +248468 +371948 +444928 +20438 +481670 +147195 +95022 +548159 +553165 +395324 +391371 +86884 +561121 +219737 +38875 +338159 +377881 +185472 +359277 +114861 +378048 +126226 +10217 +320246 +15827 +178236 +370279 +352978 +408101 +77615 +337044 +223714 +20796 +352445 +263834 +156704 +377867 +119402 +399567 +1180 +257941 +560675 +390471 +209290 +258382 +466339 +56437 +195042 +384230 +203214 +36077 +283038 +38323 +158770 +532381 +395903 +375461 +397857 +326798 +371699 +369503 +495626 +464328 +462211 +397719 +434089 +424793 +476770 +531852 +303538 +525849 +480917 +419653 +265063 +48956 +5184 +279149 +396727 +374266 +124429 +36124 +240213 +147556 +339512 +577182 +288599 +257169 +178254 +393869 +122314 +28713 +48133 +540681 +100974 +368459 +500110 +73634 +460982 +203878 +578344 +443602 +502012 +399666 +103603 +22090 +257529 +176328 +536656 +408873 +116881 +460972 +33835 +460781 +51223 +46463 +89395 +407646 +337453 +461715 +16257 +426987 +234889 +3125 +165643 +517472 +451435 +206800 +112128 +331236 +163306 +94185 +498716 +532732 +146509 +458567 +153832 +105996 +353398 +546976 +283060 +247624 +110048 +243491 +154798 +543600 +149962 +355256 +352900 +203081 +372203 +284605 +516244 +190494 +150301 +326082 +64146 +402858 +413538 +399510 +460251 +94336 +458721 +57345 +424162 +423508 +69356 +567220 +509786 +37038 +111535 +341318 +372067 +358120 +244909 +180653 +39852 +438560 +357041 +67065 +51928 +171717 +520430 +552395 +431355 +528084 +20913 +309610 +262323 +573784 +449485 +154846 +283438 +430871 +199578 +516318 +563912 +348483 +485613 +143440 +94922 +168817 +74457 +45830 +66297 +514173 +99186 +296236 +230903 +452312 +476444 +568981 +100811 +237350 +194724 +453622 +49559 +270609 +113701 +415393 +92173 +137004 +188795 +148280 +448114 +575964 +163155 +518719 +219329 +214247 +363927 +65357 +87617 +552612 +457817 +124796 +47740 +560463 +513968 +273637 +354212 +95959 +261061 +307265 +316237 +191342 +463272 +169273 +396518 +93261 +572733 +407386 +202658 +446497 +420852 +229274 +432724 +34900 +352533 +49891 +66144 +146831 +467484 +97988 +561647 +301155 +507421 +173217 +577584 +451940 +99927 +350639 +178941 +485155 +175948 +360673 +92963 +361321 +48739 +577310 +517795 +93405 +506458 +394681 +167920 +16995 +519573 +270532 +527750 +563403 +494608 +557780 +178691 +8676 +186927 +550173 +361656 +575911 +281315 +534377 +57570 +340894 +37624 +143103 +538243 +425077 +376545 +108129 +170974 +7522 +408906 +264279 +79415 +344025 +186797 +234349 +226472 +123639 +225177 +237984 +38714 +223671 +358247 +152465 +521405 +453722 +361111 +557117 +235832 +309341 +268469 +108353 +532531 +357279 +537280 +437618 +122953 +7088 +36693 +127659 +431901 +57244 +567565 +568111 +202926 +504516 +555685 +322369 +347620 +110231 +568982 +295340 +529798 +300341 +158160 +73588 +119476 +387216 +154994 +259755 +211282 +433971 +263588 +299468 +570138 +123017 +355106 +540172 +406215 +8401 +548844 +161820 +396432 +495348 +222407 +53123 +491556 +108130 +440617 +448309 +22596 +346841 +213829 +135076 +56326 +233139 +487418 +227326 +137763 +383389 +47882 +207797 +167452 +112065 +150703 +421109 +171753 +158279 +240800 +66821 +152886 +163640 +475466 +301799 +106712 +470885 +536370 +420389 +396768 +281950 +18903 +357529 +33650 +168243 +201004 +389295 +557150 +185327 +181256 +557396 +182025 +61564 +301928 +332455 +199403 +18444 +177452 +204206 +38465 +215906 +153103 +445019 +324527 +299207 +429281 +574675 +157067 +241269 +100850 +502818 +576566 +296775 +873 +280363 +355240 +383445 +286182 +67327 +422778 +494855 +337246 +266853 +47516 +381991 +44081 +403862 +381430 +370798 +173383 +387173 +22396 +484066 +349414 +262235 +492814 +65238 +209420 +336276 +453328 +407286 +420490 +360328 +158440 +398534 +489475 +477389 +297108 +69750 +507833 +198992 +99736 +546444 +514914 +482574 +54355 +63478 +191693 +61684 +412914 +267408 +424641 +56872 +318080 +30290 +33441 +199310 +337403 +26731 +453390 +506137 +188945 +185950 +239843 +357944 +290570 +523637 +551952 +513397 +357870 +523517 +277048 +259879 +186991 +521943 +21900 +281074 +187194 +526723 +568147 +513037 +177338 +243831 +203488 +208494 +188460 +289943 +399177 +404668 +160761 +271143 +76087 +478922 +440045 +449432 +61025 +331138 +227019 +147577 +548337 +444294 +458663 +236837 +6854 +444926 +484816 +516641 +397863 +188534 +64822 +213453 +66561 +43218 +514901 +322844 +498453 +488788 +391656 +298994 +64088 +464706 +193720 +199017 +186427 +15278 +350386 +342335 +372024 +550939 +35594 +381382 +235902 +26630 +213765 +550001 +129706 +577149 +353096 +376891 +28499 +427041 +314965 +231163 +5728 +347836 +184388 +27476 +284860 +476872 +301317 +99546 +147653 +529515 +311922 +20777 +2613 +59463 +430670 +560744 +60677 +332087 +296724 +353321 +103306 +363887 +76431 +423058 +120340 +119452 +6723 +462327 +163127 +402723 +489382 +183181 +107656 +375409 +355228 +430762 +512468 +409125 +270544 +559113 +495388 +529434 +38355 +422025 +379667 +131386 +183409 +573536 +581317 +425404 +350084 +472 +28532 +329717 +230220 +187196 +484166 +97434 +224595 +87483 +516998 +314876 +32610 +514586 +344816 +394418 +402330 +305993 +371497 +315790 +294908 +207431 +561014 +26584 +368671 +374990 +54747 +47571 +449424 +283761 +84735 +522127 +120473 +524656 +479659 +131627 +450959 +153300 +580908 +207785 +49115 +284991 +96505 +278306 +291655 +1404 +489304 +557459 +37740 +157465 +390475 +119166 +33871 +247428 +75905 +20779 +65035 +333556 +375415 +383676 +505243 +87327 +16451 +287235 +70190 +245067 +417520 +229234 +183786 +333018 +554156 +198915 +108021 +128262 +412443 +242543 +555050 +436511 +445233 +207886 +156397 +526257 +521357 +413043 +427189 +401614 +94823 +351130 +105945 +182314 +305879 +526197 +64409 +496800 +236461 +138175 +43816 +185904 +345711 +72536 +526737 +360400 +556537 +426053 +59044 +28290 +222548 +434915 +418623 +246454 +111801 +12448 +427133 +459117 +11262 +169045 +469996 +304390 +513096 +322822 +196371 +504977 +395364 +243950 +216218 +417217 +106736 +58194 +504101 +478522 +379314 +30432 +207027 +297146 +91844 +176031 +98287 +278095 +196053 +343692 +523137 +220224 +349485 +376193 +407067 +185781 +37871 +336464 +46331 +44244 +80274 +170147 +361106 +468499 +537864 +467457 +267343 +291528 +287828 +555648 +388284 +576085 +531973 +350122 +422253 +509811 +78093 +410019 +133090 +581205 +343976 +9007 +92478 +450674 +486306 +503978 +46378 +335578 +404071 +225558 +217923 +406217 +138054 +575815 +234990 +336257 +159240 +399516 +226408 +531126 +138599 +61693 +89861 +29504 +163296 +477906 +48419 +25595 +195594 +97592 +392555 +203849 +139248 +245651 +275755 +245426 +127279 +521359 +517623 +235747 +475906 +11198 +336101 +70134 +505447 +218996 +30080 +484457 +120441 +575643 +132703 +197915 +505576 +90956 +99741 +517819 +240918 +150834 +207306 +132682 +88250 +213599 +462584 +413321 +361521 +496081 +410583 +440027 +417284 +397069 +280498 +473171 +129739 +279774 +29370 +518899 +509867 +85556 +434930 +280710 +55077 +348793 +157756 +281111 +190689 +281447 +502854 +232894 +268742 +199553 +220808 +137330 +256903 +116017 +466416 +41635 +110906 +340934 +557501 +146767 +517617 +487159 +1561 +417281 +489014 +292463 +113533 +412247 +263973 +515444 +343561 +310200 +293804 +225867 +150320 +183914 +9707 +89999 +177842 +296524 +287829 +68300 +363654 +465986 +159969 +313948 +522779 +219820 +198352 +12959 +266727 +8016 +175804 +497867 +307892 +287527 +309638 +205854 +114119 +23023 +322586 +383341 +134198 +553522 +70426 +329138 +105367 +175597 +187791 +17944 +366611 +93493 +242422 +41842 +558840 +32203 +19667 +124297 +383726 +252625 +234794 +498228 +102906 +287967 +69021 +51326 +243896 +509423 +440124 +122582 +344325 +34455 +442478 +23587 +236904 +185633 +349841 +44294 +112568 +186296 +71914 +3837 +135486 +223747 +557517 +385181 +265313 +404263 +26564 +516867 +497096 +332351 +345139 +444304 +510877 +356387 +561214 +311471 +408789 +561729 +291380 +174671 +45710 +435136 +388858 +361693 +50811 +531134 +573605 +340175 +534988 +382671 +327047 +348400 +547137 +401037 +490711 +499266 +236370 +449075 +334015 +107234 +232315 +462953 +252048 +186822 +410168 +28994 +45550 +453626 +417957 +468577 +106338 +391684 +375143 +217622 +357903 +347648 +142182 +213843 +299148 +352587 +436676 +161875 +144655 +304741 +235017 +181799 +211042 +335507 +553731 +412531 +229740 +437129 +423830 +561806 +337666 +52016 +138057 +70254 +494393 +73119 +262425 +565395 +305329 +489611 +377080 +569450 +549766 +332940 +235302 +53893 +203781 +38449 +114870 +18699 +396338 +449839 +423613 +379767 +369594 +375812 +359219 +229311 +291675 +224907 +416885 +32964 +573406 +17282 +103375 +81860 +576886 +461334 +35672 +243442 +217269 +445055 +211112 +455675 +412384 +88967 +550643 +24223 +504074 +9275 +155546 +329542 +172658 +331600 +315492 +194208 +162867 +324614 +432017 +140860 +157944 +406616 +486079 +361172 +258346 +494140 +315384 +451014 +242619 +413684 +386187 +408501 +121089 +343603 +232538 +558671 +551596 +32992 +406647 +435260 +11156 +40896 +175382 +110560 +252968 +189694 +63154 +564816 +72004 +164788 +434583 +453104 +111878 +268484 +290768 +473215 +450620 +32673 +277479 +529917 +315868 +562419 +378347 +398637 +84097 +120527 +134193 +431472 +400238 +86426 +208830 +524535 +22213 +516813 +526044 +386193 +246672 +386739 +559252 +153344 +236123 +246074 +323615 +92644 +408621 +323231 +499940 +296105 +578902 +150098 +145015 +131431 +318618 +68409 +497928 +362520 +467755 +112702 +163219 +277289 +192362 +497674 +525439 +56267 +465868 +407570 +551608 +345211 +179653 +55295 +97315 +534041 +505822 +411082 +132375 +25378 +272008 +536605 +123511 +148737 +577712 +493751 +29587 +468297 +528458 +491058 +558976 +181421 +209685 +147545 +486964 +570516 +168662 +19446 +395997 +242911 +232511 +317035 +354527 +5961 +513793 +124390 +370123 +113397 +195790 +252813 +326919 +432414 +409239 +458221 +115667 +212239 +279279 +375554 +546622 +317188 +260818 +286021 +377111 +209868 +243148 +132037 +560624 +459721 +193498 +22623 +254164 +112841 +383470 +62692 +227940 +471335 +44858 +213649 +179898 +102837 +474078 +44478 +256197 +309492 +182923 +421139 +275695 +104965 +480780 +449749 +76513 +578591 +336695 +247474 +320490 +246105 +53183 +485740 +575823 +510735 +290741 +37017 +348708 +279784 +453634 +567644 +434192 +482719 +435324 +544299 +106896 +569926 +301574 +492885 +103462 +487151 +513585 +219647 +303685 +459645 +76292 +188579 +154883 +207728 +425074 +310493 +27221 +371694 +119404 +399665 +273556 +454577 +580698 +267664 +295769 +423740 +22461 +22667 +508443 +390401 +369997 +524627 +193349 +132223 +576743 +130586 +487741 +107542 +501420 +520109 +308156 +540581 +231362 +86471 +472930 +351133 +463605 +575577 +159842 +39504 +223020 +63525 +298627 +139883 +375205 +303549 +16838 +495680 +408112 +394474 +188044 +472143 +463751 +31481 +378139 +190853 +442614 +172006 +140270 +133051 +178028 +495090 +88455 +13232 +46323 +346275 +425905 +487013 +433136 +514402 +521906 +4157 +61418 +567205 +213351 +304008 +296492 +506561 +408120 +415961 +323186 +480379 +349199 +201918 +135023 +456483 +136173 +237917 +4972 +99081 +331569 +150007 +36450 +93400 +487461 +203629 +218093 +487181 +113935 +139512 +210981 +358883 +47419 +248382 +80357 +462663 +83097 +26159 +80429 +283055 +452676 +50159 +12326 +29430 +303264 +158122 +569070 +52925 +534876 +46975 +426376 +170293 +434417 +235517 +218476 +445008 +482774 +305632 +116848 +557252 +229270 +453485 +382214 +54759 +59171 +193328 +17152 +238071 +148531 +409725 +75434 +65358 +473057 +415408 +579415 +48636 +269606 +298784 +162799 +356400 +326854 +24601 +66499 +340247 +20992 +190218 +548464 +122203 +405306 +495376 +536028 +5713 +206831 +9395 +503939 +194440 +474253 +395849 +165141 +204935 +412621 +402922 +87141 +570664 +202622 +137362 +221737 +78947 +112129 +341957 +169562 +164780 +360216 +107641 +415015 +444955 +559102 +123070 +176592 +309366 +116461 +222075 +530470 +214363 +414487 +471567 +292123 +370210 +364243 +510254 +396350 +141524 +220310 +398604 +145436 +392476 +17482 +78032 +336171 +130812 +489743 +346638 +418854 +139072 +263860 +458240 +383443 +337533 +182334 +535608 +517946 +489924 +308117 +129945 +59973 +538364 +513458 +449433 +25165 +335851 +487688 +153834 +347612 +349689 +443688 +486008 +479149 +442286 +61108 +315338 +511546 +506444 +775 +121839 +291412 +497626 +387223 +367095 +557896 +196118 +530652 +447991 +215622 +232160 +296731 +272273 +473415 +364705 +235790 +479950 +141278 +547903 +66523 +353989 +121875 +237735 +100083 +348941 +288983 +390083 +168248 +120776 +489764 +219135 +551713 +256035 +309005 +112493 +579759 +114972 +458992 +295768 +158497 +309696 +363844 +507966 +313491 +280779 +327130 +292901 +127761 +183843 +456521 +164475 +224281 +443713 +72514 +567383 +476215 +565650 +17708 +474471 +248334 +196313 +164759 +212453 +319024 +332916 +35436 +113139 +172716 +7570 +161609 +144534 +137475 +561411 +45844 +332027 +36990 +190160 +421231 +283210 +365611 +511407 +400887 +485071 +481214 +347203 +153506 +397403 +229599 +357322 +76034 +101189 +567444 +92363 +526767 +218811 +362812 +339120 +579696 +399269 +10705 +549012 +410428 +105623 +535307 +419235 +119911 +236604 +515779 +188173 +66397 +549119 +478742 +256180 +128224 +440539 +112818 +315434 +97513 +171970 +433483 +226008 +83217 +424548 +343753 +350334 +479280 +208808 +43266 +399893 +444386 +47687 +499093 +565269 +465835 +167486 +433460 +169872 +299640 +158466 +241373 +50576 +161567 +73560 +349804 +181745 +352684 +450357 +532693 +88335 +256518 +94926 +541197 +14629 +276149 +539439 +498738 +25654 +291330 +146465 +160190 +513064 +75748 +499007 +164464 +134042 +422416 +543315 +34056 +303197 +394801 +293071 +44964 +529083 +414522 +331180 +227599 +581040 +382850 +159898 +176841 +205352 +540782 +406591 +184499 +14380 +350230 +458175 +528786 +314935 +111086 +2191 +20371 +337042 +558371 +296907 +539937 +511463 +574856 +87864 +403817 +152598 +169712 +533227 +173545 +478862 +19455 +258433 +373440 +460229 +525682 +176857 +525050 +277025 +156416 +206784 +415179 +183204 +210374 +312868 +514366 +65208 +376342 +515792 +383066 +85247 +119132 +338007 +88748 +206705 +495808 +532164 +150686 +35474 +207860 +111165 +391199 +346011 +537721 +11390 +487482 +360983 +400347 +92795 +347506 +324322 +371958 +101280 +222842 +563604 +210299 +150616 +96351 +330455 +273551 +228749 +248051 +495252 +372265 +52664 +191874 +157416 +446428 +136681 +1228 +321811 +93791 +477867 +192520 +157124 +40620 +200541 +103904 +329494 +60093 +112573 +489125 +513115 +322968 +561619 +74309 +572462 +248252 +375376 +217312 +243213 +79878 +452218 +349754 +554291 +434043 +460373 +452591 +567787 +504711 +196007 +511153 +312416 +296056 +308849 +203667 +253223 +331230 +465545 +363048 +69392 +301506 +216198 +147979 +6005 +381870 +56983 +320972 +144122 +210855 +151480 +299288 +462486 +103931 +321079 +4134 +239861 +540006 +413805 +221222 +198943 +450790 +380597 +388298 +58737 +246197 +160726 +398554 +513946 +222235 +323851 +364703 +125643 +169800 +445662 +223764 +575372 +489207 +559474 +7155 +453819 +402720 +102355 +415076 +287436 +35705 +111076 +395865 +310862 +570834 +54728 +215778 +80053 +35148 +350488 +524140 +190097 +36661 +302110 +96884 +383397 +245462 +446958 +138937 +424712 +561814 +276964 +148034 +411068 +357824 +103257 +322149 +508899 +580294 +214386 +114419 +271429 +168260 +209835 +573072 +252269 +31980 +161308 +281508 +192714 +247599 +188948 +180563 +419601 +233660 +154804 +311846 +181499 +5535 +175082 +531018 +412338 +166995 +441411 +427820 +516846 +287366 +67959 +271266 +330845 +74209 +508167 +542699 +66485 +453756 +158412 +443784 +118097 +265050 +29074 +152623 +532493 +292988 +530384 +192660 +502336 +472648 +151657 +351626 +241010 +115070 +268356 +539557 +304698 +251140 +497158 +527445 +385428 +179200 +512394 +184978 +141910 +36311 +579457 +19129 +424960 +181714 +126216 +512911 +488360 +379533 +337551 +325410 +364587 +468885 +211107 +90062 +500446 +105960 +451951 +431431 +134178 +164548 +173826 +373988 +15157 +3091 +393557 +380011 +75372 +37403 +209995 +493610 +315899 +353299 +355040 +547000 +86133 +58174 +377326 +510230 +480583 +158588 +432529 +311206 +127626 +239980 +166340 +104185 +405174 +507211 +542782 +448078 +253477 +542694 +567308 +214853 +288824 +283268 +480757 +503200 +221089 +112388 +171539 +124452 +224200 +206362 +428754 +256192 +119414 +351620 +330050 +547504 +216398 +94261 +19916 +163242 +432588 +143824 +361103 +271138 +260150 +313627 +141086 +308263 +388453 +153217 +372794 +514787 +251910 +351335 +92683 +465836 +18442 +404128 +208476 +47873 +303219 +201622 +367489 +32760 +436174 +401926 +338419 +45248 +328464 +312216 +156282 +315702 +300701 +345401 +515350 +29094 +284296 +466449 +351057 +110672 +364853 +10014 +415828 +397522 +451412 +433124 +158277 +93476 +183387 +109889 +223326 +105547 +530061 +256301 +526778 +80974 +86650 +45835 +202154 +92678 +315991 +423919 +455044 +491168 +272253 +146627 +285349 +86001 +44171 +162332 +257328 +432820 +519275 +380639 +269436 +236016 +543215 +346752 +575970 +423498 +136926 +195648 +126634 +133078 +138656 +490012 +122388 +195165 +434900 +533625 +504167 +333697 +216576 +538775 +125072 +391154 +545007 +150292 +566717 +367362 +490991 +356623 +141271 +402795 +516786 +39499 +536716 +293324 +212853 +276381 +57124 +325992 +394659 +452178 +117674 +461172 +518586 +497021 +462345 +526570 +17328 +202928 +62566 +411277 +256983 +49473 +211206 +398031 +277955 +531178 +453959 +27946 +252844 +30273 +536933 +500298 +229111 +7977 +27642 +303726 +79927 +110313 +527691 +442205 +33345 +365851 +233236 +239157 +409221 +400803 +32947 +422516 +359727 +215872 +559454 +289716 +450247 +57827 +312298 +530383 +260048 +35857 +224222 +299533 +13296 +325907 +117869 +54088 +391011 +340478 +205344 +347823 +468604 +78701 +101414 +197499 +490871 +89273 +380343 +441974 +35974 +486114 +354398 +535536 +294030 +7276 +278742 +137028 +98721 +372764 +429802 +72105 +220307 +116845 +195406 +333000 +130401 +264382 +125458 +363036 +286994 +531070 +113801 +4108 +47603 +130118 +573924 +302990 +237566 +21470 +577926 +139436 +425925 +36844 +63602 +399791 +35894 +347228 +225617 +504813 +245320 +466007 +553931 +166731 +164885 +19090 +457262 +247806 +502895 +167593 +352491 +520 +26386 +497348 +352000 +386164 +32901 +730 +30925 +333167 +150361 +231747 +462244 +504958 +260738 +313762 +346645 +486118 +202998 +541613 +183884 +230245 +83172 +126638 +51844 +421673 +118625 +377723 +229427 +371326 +104345 +361687 +114246 +397354 +104137 +120850 +260516 +389168 +234555 +26348 +78522 +409784 +303024 +377949 +69887 +546983 +113736 +298197 +476810 +137315 +376321 +410337 +492905 +119785 +158167 +185930 +354061 +106563 +328452 +506587 +536517 +480173 +570688 +376441 +252127 +247720 +132554 +41923 +400317 +170041 +151938 +198650 +6437 +49091 +221820 +455966 +309859 +300659 +15850 +388014 +253386 +65415 +238228 +548882 +302155 +93483 +371869 +397287 +315249 +360564 +448410 +21382 +477474 +144862 +517515 +230190 +322353 +231568 +14940 +132719 +498942 +182469 +113720 +168890 +94852 +246077 +117535 +52596 +419116 +522020 +255338 +125228 +564332 +106375 +249534 +220915 +177758 +293057 +222430 +196878 +554980 +375606 +173081 +84936 +418907 +562229 +457616 +125700 +66038 +239274 +574110 +305540 +98431 +167347 +53345 +438481 +286010 +5569 +343606 +168898 +191301 +236338 +291394 +715 +520237 +236954 +192212 +524002 +471625 +476029 +413124 +203455 +483328 +476417 +114389 +372428 +369221 +322654 +388157 +561314 +264540 +418680 +359540 +426182 +521613 +92248 +74478 +398905 +554273 +125909 +430583 +418959 +503522 +382999 +403145 +536375 +352618 +108193 +279696 +163253 +439007 +204536 +552186 +269926 +372147 +399921 +201418 +240565 +471483 +91619 +393971 +331648 +385856 +567440 +81922 +391722 +372894 +535997 +134096 +545958 +239943 +186929 +34222 +177714 +277812 +197111 +281878 +532003 +557172 +142890 +196116 +385454 +322845 +374987 +123137 +255112 +111207 +304819 +523526 +336046 +42893 +241273 +240049 +90659 +271364 +408008 +253282 +167067 +354278 +178317 +229653 +93333 +163666 +566920 +495199 +100329 +218119 +558864 +257382 +406152 +206587 +420339 +325919 +278853 +555763 +293200 +151000 +209664 +79380 +197177 +353953 +464522 +392260 +46144 +154202 +164366 +206025 +511236 +24921 +497907 +393226 +318138 +364125 +157321 +492395 +187857 +109939 +441500 +144251 +368581 +51403 +283498 +43555 +89356 +404601 +23272 +425762 +460682 +544629 +209829 +322029 +199247 +307262 +571242 +124236 +162393 +104829 +250766 +563938 +237399 +131516 +483001 +21994 +97958 +540187 +264497 +384808 +343187 +51277 +6712 +566103 +435384 +292082 +359039 +165157 +267972 +263796 +489313 +392722 +541924 +554433 +571034 +146112 +201934 +518716 +64116 +294992 +289586 +159970 +479617 +269006 +140465 +513260 +554805 +6579 +452696 +34445 +548296 +372983 +509656 +199339 +130030 +128372 +449454 +139306 +247914 +99024 +499134 +536653 +468917 +412813 +404338 +215303 +455414 +413497 +574988 +397117 +188631 +378701 +241867 +143129 +419884 +412749 +496954 +317732 +16977 +398309 +162363 +147576 +100016 +209018 +92660 +173302 +525732 +449198 +99734 +12733 +172946 +168032 +210988 +340697 +4795 +534887 +483553 +278323 +178175 +190095 +357542 +230432 +227460 +334609 +562121 +378126 +555357 +325666 +451859 +526837 +531710 +297249 +294839 +499785 +254976 +527220 +173057 +11760 +163012 +215998 +114420 +57812 +563712 +513887 +201859 +36333 +291990 +338375 +460621 +518889 +337502 +133050 +80172 +537007 +295270 +335644 +227852 +336044 +204137 +82259 +165675 +295713 +343937 +442567 +356002 +346932 +62985 +180925 +525381 +13081 +377406 +159774 +462643 +359105 +185821 +390201 +84168 +128059 +80340 +481159 +491902 +306619 +353807 +390569 +541562 +292616 +64621 +439224 +96288 +449798 +160927 +496324 +90778 +126145 +97230 +572767 +11570 +539075 +350988 +3779 +208135 +551315 +216449 +169606 +502 +67765 +281414 +118594 +146127 +543985 +124927 +471394 +385508 +373783 +501315 +140974 +42757 +527054 +202387 +513056 +329931 +153973 +510152 +520812 +534601 +131282 +386638 +508538 +234779 +229329 +396568 +153568 +229478 +153574 +356299 +436694 +324139 +299409 +212462 +478155 +393266 +117836 +190760 +213605 +196 +444382 +445211 +363845 +433277 +521141 +464786 +169076 +301402 +4495 +177258 +328962 +183757 +452966 +416059 +113233 +559417 +280678 +481398 +328372 +234910 +30667 +343062 +383046 +370953 +258089 +404229 +456931 +535183 +300867 +60507 +262672 +7288 +81100 +575395 +539951 +347848 +437594 +352005 +14941 +196453 +528386 +466939 +482187 +293468 +494077 +217285 +362951 +435751 +411480 +517315 +480015 +60610 +353001 +376442 +430265 +478338 +303069 +525344 +437331 +389315 +8179 +31981 +313872 +330920 +515465 +258905 +142249 +323128 +389699 +565012 +124636 +488693 +376608 +309424 +370596 +261940 +39871 +226984 +152866 +515050 +116861 +412876 +120411 +550452 +565273 +273791 +181466 +183155 +293505 +336113 +569997 +303738 +331049 +147030 +74058 +198176 +23991 +198841 +79816 +85183 +261535 +566756 +386291 +318200 +569849 +57429 +36049 +420827 +519271 +24391 +172087 +158795 +133002 +522198 +133698 +499365 +79261 +258860 +457718 +179948 +421875 +558073 +206684 +529762 +456756 +65773 +425722 +53102 +294264 +416730 +38574 +176275 +404297 +127494 +242060 +272212 +189244 +510861 +421370 +208516 +206431 +248457 +39502 +375087 +130839 +308730 +572453 +263474 +544611 +255708 +412604 +390094 +578131 +234463 +493563 +9450 +381914 +148999 +32300 +423576 +569758 +347253 +92939 +112212 +13923 +39472 +363736 +289659 +269949 +88349 +188522 +488915 +129054 +573823 +316000 +440562 +408818 +539302 +199575 +122300 +340047 +322816 +472878 +313922 +228071 +265648 +400166 +169166 +10040 +125245 +148766 +31281 +172599 +431067 +208236 +441824 +175611 +15148 +431199 +521587 +50025 +443139 +349822 +515056 +27530 +571970 +82367 +7115 +424333 +157601 +537506 +447187 +115182 +547597 +5586 +143040 +31650 +196336 +279818 +206273 +403104 +514248 +243190 +558642 +548246 +16848 +391539 +89614 +284589 +191314 +259452 +208380 +209441 +465463 +385005 +321385 +223569 +11727 +87574 +566470 +210890 +323598 +427193 +425676 +401240 +94021 +259571 +447553 +456053 +84693 +14278 +119995 +234595 +408696 +136271 +143560 +357578 +28071 +36561 +157102 +293789 +392251 +356622 +180274 +48320 +475779 +301326 +100977 +413551 +574010 +404479 +80725 +552221 +575441 +197424 +124601 +215633 +359546 +25386 +73199 +334466 +156572 +124614 +34121 +460049 +327623 +441695 +292488 +476514 +464018 +348571 +113413 +125208 +129690 +446218 +493761 +383413 +460390 +343149 +374041 +525211 +451263 +333683 +385194 +107427 +102872 +517249 +475879 +575755 +147787 +297180 +343774 +112437 +142240 +384503 +511111 +51089 +145408 +143582 +408138 +162858 +71850 +126925 +222781 +314616 +425609 +203928 +337563 +223300 +52644 +272566 +232597 +374430 +469075 +267164 +265851 +28134 +308889 +465795 +47263 +233727 +42 +493117 +124621 +533378 +361259 +458750 +429033 +383289 +490927 +520964 +174420 +64425 +378859 +401850 +281475 +46508 +205300 +280736 +110961 +230679 +151956 +321497 +73665 +488736 +165353 +365983 +556230 +21465 +581226 +448861 +3793 +347335 +150726 +75319 +2521 +285894 +133876 +104589 +346013 +63516 +83656 +491515 +326256 +49942 +28508 +475413 +270222 +235839 +48554 +327777 +111179 +507171 +425973 +449490 +205239 +82375 +459575 +432300 +91885 +340922 +270239 +195894 +121417 +344831 +439651 +232148 +391688 +480793 +534275 +260823 +469294 +8688 +255654 +191300 +383464 +81594 +21240 +478077 +517596 +555953 +294119 +402234 +459500 +564280 +106849 +167501 +98328 +267411 +145512 +272599 +50054 +414156 +161129 +418226 +11796 +502090 +390350 +440500 +240727 +104406 +163682 +437910 +143767 +358901 +527631 +500543 +28377 +231097 +227985 +556703 +421566 +73201 +478393 +280347 +15497 +131969 +515760 +295440 +462527 +42147 +120007 +212895 +425361 +454143 +5758 +366782 +213932 +229848 +458861 +132791 +476664 +150365 +343038 +529649 +180515 +499810 +329041 +15660 +419228 +396295 +502644 +321085 +245049 +34193 +217323 +446455 +528046 +375573 +15802 +147448 +407291 +84000 +280891 +150487 +510606 +163025 +249964 +126123 +233771 +118507 +97278 +357386 +23121 +10580 +2153 +176017 +371472 +373289 +173908 +296797 +334083 +301107 +577522 +125404 +278359 +575032 +273002 +266371 +108315 +255633 +503490 +250051 +143927 +117407 +198271 +447043 +329789 +399991 +458388 +87489 +228411 +494634 +260802 +454161 +446322 +231079 +438373 +395665 +244539 +212427 +356660 +347276 +183287 +498374 +21167 +544522 +418533 +288493 +245660 +406103 +406976 +367313 +455555 +117337 +384465 +185697 +160393 +463825 +276852 +181462 +176288 +452816 +102497 +54277 +225791 +361046 +197278 +9857 +227736 +398992 +55868 +170914 +181677 +467803 +560470 +264599 +540372 +559442 +201207 +137227 +267643 +355471 +245431 +555669 +344498 +84783 +193474 +102411 +401860 +119469 +448786 +449990 +568082 +340472 +307573 +231828 +307547 +82052 +15140 +493612 +503972 +386592 +473219 +495557 +159440 +355869 +311531 +209733 +240119 +415048 +296098 +249482 +15663 +151432 +263011 +488539 +463913 +502798 +174276 +495613 +407861 +229304 +146742 +545039 +161202 +295134 +162144 +453317 +52759 +335201 +222903 +20333 +559550 +336049 +346140 +491223 +306611 +102746 +455355 +449921 +477288 +77821 +289712 +452663 +147758 +129571 +490869 +345961 +94501 +160394 +432993 +178796 +372494 +316323 +383435 +194940 +74583 +148911 +518027 +431827 +32724 +158548 +227227 +500330 +54679 +321024 +471175 +252074 +476569 +573258 +337247 +294373 +558661 +148898 +563267 +163112 +411968 +193565 +455210 +349344 +337160 +160456 +255158 +553678 +123843 +549687 +381968 +579471 +100604 +379841 +357526 +197263 +14756 +412639 +210915 +47204 +539251 +166255 +490199 +260363 +91654 +170550 +187888 +97362 +285418 +176993 +292741 +361901 +296988 +223496 +493753 +114907 +151358 +316534 +472509 +499802 +348519 +347747 +58851 +104790 +396779 +130528 +2255 +19624 +526800 +233950 +505945 +131207 +290750 +114090 +196665 +8708 +134688 +394715 +115088 +492196 +530099 +518729 +291572 +421457 +445365 +78929 +415461 +551796 +210002 +207913 +344878 +303893 +149196 +353275 +122413 +553361 +519132 +467135 +431439 +17089 +322119 +228214 +35062 +105689 +366141 +285651 +60409 +472671 +401446 +492846 +21023 +421952 +374100 +265200 +506628 +62298 +243626 +212122 +350648 +409921 +428140 +399212 +388267 +198921 +429246 +202040 +570001 +261346 +61171 +131815 +455448 +82696 +554607 +102174 +386803 +188421 +191846 +209898 +380117 +321064 +119617 +188651 +132210 +244299 +174072 +542910 +378334 +118405 +543347 +183657 +581180 +395289 +64760 +265584 +29573 +493720 +94795 +315601 +416596 +260106 +244019 +463884 +579468 +112085 +300972 +238528 +382542 +57672 +165298 +46889 +289497 +337180 +481252 +7913 +432150 +288161 +403758 +257336 +565331 +346589 +270785 +205670 +231580 +508580 +98871 +239997 +554579 +160057 +404922 +78771 +380756 +171199 +148077 +22892 +145378 +26967 +235200 +176007 +90349 +554377 +189744 +257053 +270515 +66508 +113890 +291983 +558927 +420916 +140908 +58384 +438226 +575776 +106935 +40602 +468993 +494810 +210408 +365685 +483722 +39430 +258793 +272615 +51476 +189919 +443887 +391648 +422670 +445135 +198959 +405529 +459757 +465489 +81827 +262576 +408289 +309237 +76249 +460091 +512630 +45959 +280320 +200492 +404652 +48475 +18480 +457097 +65889 +162256 +265950 +520752 +299082 +51500 +499313 +104906 +35438 +167647 +7274 +387824 +242139 +173166 +399830 +12014 +510642 +154053 +67785 +78170 +514118 +87998 +52703 +203539 +534533 +85926 +274438 +401653 +458790 +509262 +144481 +387515 +246649 +503207 +235131 +501531 +62025 +43286 +272323 +326128 +561889 +167529 +171067 +50778 +301282 +469719 +509388 +480317 +379055 +546428 +192763 +445602 +420882 +232790 +174332 +232865 +292822 +511145 +119502 +312591 +110330 +281353 +116244 +58778 +428079 +64902 +520840 +232054 +473214 +572574 +296684 +351590 +217997 +178761 +71618 +226496 +285212 +381195 +499903 +232849 +468997 +345559 +503097 +578570 +396404 +405223 +578752 +403500 +188958 +504498 +491623 +462929 +525762 +395550 +574227 +240751 +169356 +524694 +40886 +571635 +487774 +86220 +95677 +268987 +502599 +155270 +103855 +125100 +241355 +220214 +391774 +110618 +154587 +134483 +458781 +360877 +465963 +194595 +346934 +127153 +188078 +553869 +102665 +400547 +33759 +42779 +397587 +140295 +151807 +549136 +470288 +89738 +328368 +546934 +164255 +563683 +399988 +360951 +217303 +326781 +546133 +135399 +94666 +330037 +569839 +411070 +497466 +404805 +417854 +318442 +255036 +457230 +346863 +307438 +370448 +5124 +152582 +38118 +12179 +58462 +308420 +329456 +74920 +250368 +186428 +556073 +111806 +361244 +80273 +230964 +156754 +503101 +75173 +389404 +195538 +88848 +286018 +245481 +140929 +533721 +268378 +70048 +315467 +46269 +372807 +192403 +387328 +163033 +481314 +65306 +192529 +321107 +112232 +441216 +412399 +565391 +220670 +61471 +463290 +346707 +67587 +147624 +13031 +396754 +278601 +439426 +42834 +281829 +376209 +353148 +556562 +97579 +217989 +319530 +82551 +235319 +431799 +53892 +52853 +54533 +88897 +225093 +386777 +546742 +273684 +413900 +245447 +577995 +16249 +188414 +485142 +199602 +89258 +109679 +502397 +14494 +13632 +51674 +244999 +305050 +455956 +426795 +560700 +327306 +410301 +343803 +539422 +156740 +527845 +100582 +9941 +466585 +61515 +231895 +157052 +41271 +148128 +141172 +320232 +78565 +539883 +391300 +365182 +322194 +116517 +323496 +473783 +519874 +440706 +361587 +265153 +329946 +342814 +32258 +153510 +194555 +309317 +245006 +300303 +97767 +218224 +370170 +290477 +207178 +456730 +209480 +513775 +199516 +581542 +32524 +416337 +96241 +506279 +422893 +248911 +509855 +355183 +201220 +234914 +333436 +68198 +429074 +328430 +160531 +467854 +280688 +140661 +349525 +267315 +565543 +313162 +25751 +232574 +560358 +505213 +494427 +160308 +287335 +99182 +413260 +558808 +290839 +122954 +229221 +192007 +243189 +117645 +552824 +366111 +102056 +356949 +566298 +97899 +422545 +343769 +13127 +179273 +104486 +37660 +304099 +517570 +20207 +36484 +36492 +155974 +107257 +534019 +522371 +222825 +96183 +509227 +302260 +95078 +280918 +367582 +317033 +347982 +73209 +290521 +187243 +425151 +483723 +573796 +187249 +144114 +132992 +35887 +546067 +426532 +45626 +461805 +129989 +541478 +485489 +578498 +485483 +144784 +248224 +372362 +92050 +423519 +473118 +177207 +105455 +276434 +157767 +384335 +509497 +338191 +224010 +327388 +96988 +43376 +67867 +320743 +555197 +104453 +14439 +512194 +396387 +252559 +108953 +461262 +66320 +97946 +238065 +306139 +572408 +577864 +81004 +464526 +89378 +193389 +259049 +85665 +381134 +412419 +308947 +557510 +502084 +288290 +254609 +188752 +439525 +13980 +140513 +240173 +305268 +38678 +394050 +402926 +364079 +159260 +293034 +55429 +289640 +291028 +211120 +48050 +93887 +361029 +486026 +388374 +207803 +540174 +530630 +430359 +36420 +120099 +199764 +492911 +84498 +200882 +139843 +4975 +421209 +259513 +520324 +211317 +236457 +419344 +3867 +287846 +50434 +26624 +507235 +16238 +103705 +497555 +440060 +175825 +245460 +308276 +178535 +391735 +206391 +201550 +400945 +194634 +262360 +554142 +407574 +225225 +246057 +498627 +486172 +226571 +461751 +459733 +345869 +503841 +286460 +45644 +22861 +285599 +580284 +569565 +286778 +150024 +542101 +484075 +538153 +20470 +128034 +544120 +357109 +450728 +550968 +326230 +558809 +76334 +555387 +47121 +523978 +11081 +378134 +116279 +364884 +488250 +551957 +322824 +545564 +255573 +286327 +355453 +361933 +434897 +32597 +226761 +166482 +557564 +208166 +232115 +283520 +137395 +555894 +103509 +174284 +458313 +316147 +344059 +370701 +548930 +89894 +373662 +572095 +19324 +574411 +45746 +480122 +63950 +92339 +201111 +157053 +401539 +427956 +339099 +274651 +159537 +556101 +323399 +564337 +514915 +556025 +66427 +322357 +173737 +369128 +420230 +45176 +509675 +374677 +272311 +109797 +384723 +383678 +453040 +91080 +301634 +533003 +40361 +221605 +216228 +104002 +161011 +146123 +214421 +496252 +264948 +9759 +138856 +316189 +145734 +50411 +325157 +259099 +516856 +529668 +135976 +467130 +367433 +385598 +520933 +102805 +30066 +436696 +216837 +380754 +350457 +126974 +565374 +73832 +214703 +110501 +380609 +135872 +140231 +251816 +133836 +398866 +230362 +426815 +2240 +51484 +546325 +224093 +221190 +525024 +238806 +99908 +165795 +109146 +537727 +496571 +183803 +211175 +433845 +168692 +526394 +368402 +256309 +468972 +139169 +398440 +171678 +547341 +64332 +533589 +483249 +406000 +330348 +439188 +572886 +252829 +242724 +139127 +404568 +45809 +52257 +458727 +334509 +559665 +60992 +290896 +503106 +27972 +536891 +410855 +31202 +457882 +403315 +87399 +395291 +322141 +226377 +202799 +420826 +553034 +212077 +97693 +266370 +101656 +504142 +342933 +87567 +342060 +268854 +437028 +20175 +198625 +405047 +382374 +338291 +403975 +527906 +322429 +545550 +140043 +107389 +74059 +315621 +110138 +78381 +295576 +494438 +106335 +472349 +15818 +162358 +366484 +44604 +66524 +118606 +366873 +270721 +556478 +350789 +298628 +163314 +262800 +459428 +491725 +285421 +406332 +498280 +34535 +524282 +315744 +226592 +218294 +459141 +242034 +114164 +293733 +248242 +452881 +441496 +54358 +177489 +372861 +349489 +483941 +572802 +356494 +193875 +146570 +58253 +21338 +6220 +341933 +533368 +1818 +428248 +293026 +227656 +193021 +326938 +512966 +226020 +343059 +249720 +540106 +375278 +300023 +126512 +517135 +472540 +361439 +132702 +503294 +109537 +540669 +332007 +245266 +313999 +10386 +225715 +311567 +103837 +302405 +248616 +102654 +155087 +124756 +379659 +569272 +160166 +428234 +422280 +174425 +133412 +174503 +216581 +345063 +52949 +69536 +216161 +272728 +200870 +120792 +193480 +493923 +445567 +558539 +51938 +422706 +416271 +244160 +437898 +327352 +305480 +349459 +522418 +485219 +225133 +361400 +546569 +190015 +348216 +421822 +457683 +178683 +40894 +234526 +465074 +518725 +168096 +210190 +139605 +35195 +463640 +286770 +141651 +112022 +532552 +325327 +227224 +17272 +84163 +331475 +126065 +289309 +8583 +52952 +189427 +579693 +437947 +187565 +215982 +356424 +453731 +463522 +372316 +251797 +70187 +280515 +556608 +341635 +391067 +469480 +476298 +57917 +146672 +122747 +394328 +12209 +80013 +573291 +278449 +129659 +579560 +557190 +227468 +334782 +51157 +23774 +9426 +86582 +39211 +275751 +131597 +51250 +357255 +9041 +346482 +9647 +157019 +409016 +273416 +114414 +298172 +388854 +275025 +58079 +518034 +503518 +146710 +120632 +474680 +303713 +259097 +479630 +208318 +437298 +173704 +361831 +371638 +344279 +230175 +72507 +417980 +72621 +163057 +92894 +543525 +577364 +263696 +472732 +66027 +391584 +197745 +131019 +65604 +91318 +535934 +212646 +576354 +482071 +160556 +120129 +7260 +344881 +447548 +318193 +30383 +527002 +34904 +35677 +526222 +105261 +401897 +399452 +25660 +524595 +384512 +117543 +514600 +268944 +112664 +222340 +569058 +495332 +192153 +75591 +286711 +174888 +577065 +25508 +169972 +401820 +425475 +290700 +173091 +559101 +122418 +244124 +198645 +325519 +276437 +528276 +146614 +45574 +417804 +326420 +250594 +27353 +310407 +370103 +274957 +561160 +167598 +397166 +257458 +404546 +148392 +373396 +62230 +493522 +563665 +274240 +269815 +79024 +527427 +84674 +486788 +267690 +443347 +149304 +412285 +207041 +412916 +10764 +151338 +299000 +17882 +475510 +398188 +558213 +70493 +180779 +347210 +280211 +58146 +379022 +504125 +537604 +464858 +329573 +568623 +228309 +454444 +552775 +557884 +435671 +168706 +142257 +571437 +574845 +387773 +321008 +574208 +405811 +375426 +321887 +256852 +433554 +517029 +125870 +80395 +497139 +490008 +405279 +571857 +225738 +514913 +456239 +499402 +96440 +487607 +370999 +319617 +370233 +60760 +352703 +478575 +84170 +134112 +77689 +185036 +73738 +547502 +104782 +213276 +136908 +436273 +442149 +355000 +374061 +249884 +105711 +136464 +146997 +76351 +388487 +99115 +124135 +24721 +132931 +1149 +182403 +386089 +81691 +480657 +441522 +60989 +268000 +55840 +514321 +577959 +359638 +457986 +533596 +60332 +367082 +772 +535842 +473541 +270677 +409009 +259216 +302318 +117036 +331372 +231125 +384486 +405214 +20760 +579760 +172995 +359110 +83110 +410068 +109916 +328757 +299261 +19028 +515660 +40757 +10256 +442695 +553097 +185903 +74388 +425120 +241326 +299609 +29397 +328728 +283881 +344029 +367336 +27075 +163628 +127263 +488979 +460147 +473050 +405762 +221547 +131581 +561187 +406489 +140696 +452721 +530466 +118965 +398803 +218365 +298738 +19441 +521550 +120157 +498687 +4754 +365866 +70865 +235156 +133386 +142742 +221183 +262391 +567053 +520982 +121349 +448779 +440354 +3983 +578993 +519691 +160703 +103307 +300408 +137106 +488377 +523660 +318022 +132578 +302520 +153040 +408817 +145227 +311190 +159662 +202923 +256775 +359864 +384848 +336404 +185303 +421703 +362682 +464622 +246590 +422729 +165500 +42563 +219216 +520232 +95063 +265547 +532686 +290558 +112591 +448211 +315281 +545475 +225850 +232460 +82740 +272880 +347254 +122047 +352151 +541486 +97249 +200252 +544782 +499571 +379014 +303534 +479909 +305464 +323682 +181524 +273855 +190783 +567801 +119752 +241503 +536429 +327323 +128756 +349868 +500495 +372260 +315824 +484986 +364993 +124759 +300124 +329319 +68628 +14549 +121897 +506595 +115709 +199610 +230150 +31717 +139549 +222332 +534161 +360393 +541664 +507167 +286523 +158660 +66926 +195750 +80022 +589 +252220 +47255 +247014 +49881 +455005 +232453 +445722 +516805 +544122 +541917 +469356 +370042 +130522 +502163 +307866 +408894 +524247 +52233 +177861 +348881 +357943 +295303 +475389 +431691 +61316 +143998 +503483 +340155 +488785 +133636 +133567 +251627 +470095 +34873 +88815 +261178 +468612 +127477 +157960 +15687 +303089 +572331 +456708 +190515 +126131 +239194 +332074 +129765 +107167 +478184 +421833 +359715 +112440 +331317 +74492 +505386 +247839 +534210 +134503 +422700 +352111 +98674 +546219 +520508 +503008 +461953 +101913 +362092 +22103 +359128 +316666 +335579 +414750 +297980 +365652 +53635 +547601 +97589 +570515 +7125 +99828 +321437 +80671 +426275 +294883 +212605 +424293 +338108 +25005 +6949 +234291 +428399 +7149 +343076 +575287 +431848 +307611 +293909 +542511 +564739 +573843 +356878 +472864 +336793 +121904 +161060 +254004 +269873 +216428 +77172 +346517 +498555 +203690 +348973 +117704 +552672 +275270 +208107 +314016 +427518 +278134 +53420 +318777 +238980 +350614 +467315 +61233 +272188 +550797 +125051 +553965 +187286 +282912 +102532 +156076 +467848 +130875 +531585 +523470 +507684 +332582 +438989 +489209 +125944 +127474 +371957 +570349 +283286 +541635 +547106 +253630 +388677 +572525 +542302 +554537 +367205 +228300 +443498 +356432 +123946 +490441 +211063 +224542 +116574 +434510 +33116 +353136 +134167 +128291 +542510 +433963 +147453 +365766 +374806 +336600 +38238 +165476 +535578 +127788 +157099 +173640 +114348 +496722 +58141 +467296 +235864 +5154 +22775 +422536 +136820 +453438 +446359 +41990 +422240 +39267 +391392 +233825 +308504 +478250 +87328 +4079 +127074 +267709 +377635 +353231 +185768 +487897 +124215 +249757 +341681 +557552 +280733 +374734 +281601 +456420 +222266 +491947 +432732 +467157 +94025 +410328 +428291 +397639 +163528 +234697 +557573 +208363 +515962 +358658 +373075 +438995 +425672 +450169 +216103 +254638 +288591 +53626 +43417 +372252 +5038 +218357 +120860 +399349 +485509 +530261 +477087 +352302 +96075 +495443 +133928 +197175 +134074 +212553 +448181 +152000 +254277 +105734 +75481 +343662 +479350 +554347 +71090 +297426 +22176 +277622 +469235 +163041 +221272 +154263 +89296 +68411 +192871 +183217 +258141 +53058 +540529 +566414 +560948 +254535 +246076 +135972 +420069 +431023 +343643 +32682 +515176 +222635 +377155 +547041 +513283 +26017 +366096 +252133 +138078 +25685 +321798 +549361 +14088 +423048 +570810 +374974 +447501 +492544 +554046 +575357 +420791 +6019 +340451 +66800 +565575 +148055 +330432 +483038 +455004 +288765 +11034 +86988 +347142 +450559 +543581 +293757 +556901 +533032 +333020 +260266 +22420 +13948 +512657 +214124 +231236 +177149 +560879 +491793 +35767 +312878 +118542 +450596 +423773 +48653 +224523 +509577 +462677 +75405 +350023 +452122 +42008 +302555 +382309 +468483 +368684 +372580 +31333 +153697 +124876 +330023 +315672 +53990 +136533 +82815 +356836 +414821 +268717 +7333 +77544 +525373 +371042 +227048 +576327 +419309 +239773 +8119 +424135 +297425 +222711 +489909 +393995 +31019 +539326 +517612 +102461 +199989 +483374 +44952 +103863 +528980 +441543 +85381 +247234 +50924 +483994 +87456 +424271 +356091 +534669 +378831 +560662 +298773 +257896 +498274 +305800 +40517 +183949 +276840 +84442 +297620 +298252 +119088 +233315 +283977 +345154 +287649 +427311 +63399 +4700 +463611 +224104 +209388 +431655 +364190 +28864 +412455 +283290 +228541 +422200 +985 +133596 +323853 +503081 +130732 +224675 +199688 +230862 +21396 +485390 +1532 +125778 +235541 +370478 +522478 +514292 +384338 +531707 +178746 +532747 +62915 +519491 +140691 +112093 +358024 +263687 +297595 +506085 +102446 +325768 +29558 +222054 +466965 +316254 +546500 +216785 +194184 +464390 +348371 +231582 +208995 +464339 +308856 +340946 +214604 +570586 +182227 +248441 +89078 +376310 +73450 +115924 +308235 +15994 +8749 +429679 +37751 +122040 +284286 +388707 +248163 +11320 +427997 +282062 +237600 +376751 +223314 +86215 +12443 +163255 +564940 +462640 +522713 +306303 +460675 +126833 +26201 +224757 +357899 +546782 +96427 +480944 +479556 +569273 +520528 +190690 +344832 +462466 +270354 +559776 +279259 +280909 +227781 +163798 +491098 +439658 +416088 +107375 +74132 +379800 +511654 +346687 +226161 +578849 +544272 +146149 +570624 +178299 +126671 +356380 +530766 +175954 +158798 +422095 +55780 +512276 +560626 +187329 +513125 +347216 +306486 +161840 +180917 +188192 +421437 +93120 +324891 +252216 +488476 +578347 +101959 +10693 +170038 +213586 +210439 +469202 +381463 +343248 +127785 +287328 +538690 +16382 +293022 +112378 +435785 +56092 +381504 +284365 +406129 +233119 +53629 +188509 +191053 +81056 +82252 +538319 +38439 +181948 +439710 +529344 +434035 +342958 +563882 +37734 +364743 +330986 +546226 +463211 +62210 +442724 +232241 +293858 +119345 +61953 +577033 +522015 +381587 +350107 +4936 +511307 +228771 +177811 +231450 +176168 +84540 +259408 +264238 +539738 +255827 +459382 +221105 +431742 +204337 +227741 +336356 +37655 +167159 +59352 +165937 +53956 +378712 +88462 +495786 +542938 +566498 +367228 +157577 +442661 +62363 +390689 +480664 +521540 +414249 +20571 +160855 +451683 +156832 +570045 +326542 +568276 +568717 +563311 +113579 +218268 +546095 +160661 +341118 +150649 +462632 +198972 +220025 +61720 +430681 +524011 +457217 +40064 +285583 +314493 +78023 +470882 +298722 +555597 +489829 +314779 +367818 +138503 +243737 +580255 +444565 +386677 +190841 +493074 +234347 +466988 +227033 +519039 +351554 +390585 +443303 +140983 +81079 +538005 +169757 +368780 +457322 +341804 +409116 +181805 +284292 +551358 +344548 +503569 +336587 +417055 +522315 +58705 +148955 +375530 +474934 +577893 +28881 +360772 +445267 +244737 +355777 +72811 +190788 +54513 +243075 +518551 +487530 +292169 +69293 +397303 +129285 +429996 +109532 +53802 +340573 +91280 +535602 +270908 +381925 +549220 +488573 +47131 +32735 +117525 +279085 +43961 +188906 +394677 +395 +185201 +189365 +127596 +32712 +504810 +3703 +182874 +146981 +306755 +453093 +520503 +169808 +225670 +91063 +348584 +461802 +572555 +185922 +131497 +46736 +536006 +256505 +214975 +13445 +350736 +98115 +50304 +361180 +511333 +564820 +429717 +222500 +40083 +538230 +349438 +371250 +528578 +240418 +302380 +261758 +535809 +308388 +578878 +509451 +46919 +562592 +499950 +90374 +318146 +195353 +355325 +314515 +237277 +203024 +238911 +32039 +145591 +16030 +135411 +229350 +421757 +48034 +183704 +307292 +97974 +275999 +448256 +451915 +119113 +143503 +494141 +50124 +306553 +35526 +255279 +560908 +247264 +367599 +192782 +511324 +574350 +67569 +204360 +111907 +2839 +513971 +245201 +185240 +339468 +540101 +539673 +194425 +22168 +520150 +301595 +96006 +68286 +131280 +356662 +182441 +284749 +107108 +49761 +386718 +55244 +187990 +248678 +147721 +425727 +360350 +310797 +76765 +400489 +247639 +279864 +44699 +356145 +69138 +445041 +560598 +165464 +536343 +7818 +322831 +334760 +451463 +348730 +285967 +286353 +201887 +166165 +359 +465591 +519359 +550444 +402711 +3661 +132706 +534983 +306281 +150317 +15978 +580029 +496090 +267127 +210980 +384015 +222559 +2235 +255649 +278168 +440840 +27326 +202562 +230268 +362712 +1573 +107661 +464515 +373132 +447242 +547440 +43613 +200143 +260883 +250901 +64693 +408480 +204757 +319933 +147471 +381332 +518197 +27656 +260257 +434580 +159203 +568630 +497441 +499597 +60179 +574804 +343254 +501762 +220704 +524536 +86946 +456046 +62937 +49633 +144305 +475593 +478553 +574145 +63648 +3794 +303177 +1340 +82835 +371427 +156747 +448694 +219567 +75095 +242615 +492077 +132776 +199125 +349622 +195754 +455548 +181873 +138185 +338044 +362797 +180953 +505826 +69773 +304834 +162580 +154090 +519853 +319687 +132328 +27969 +52166 +100547 +568131 +415218 +348045 +478159 +402869 +10211 +26547 +551692 +105432 +313340 +182348 +383419 +570947 +345353 +226883 +255784 +214199 +262262 +283261 +449708 +299970 +392391 +245997 +330410 +343571 +519542 +37470 +42144 +342521 +498537 +10935 +443860 +512648 +146099 +98599 +123932 +489861 +262895 +184700 +218587 +363581 +21001 +481404 +249356 +64240 +492349 +199236 +481064 +353405 +116479 +132024 +138768 +524665 +434511 +326970 +138784 +340368 +312081 +366615 +171942 +21232 +473850 +93686 +295574 +51054 +162692 +174091 +20070 +270066 +492816 +20904 +484500 +147140 +242972 +420081 +63563 +261712 +316396 +49413 +520787 +510955 +393840 +142487 +19817 +261180 +413736 +230619 +484614 +337011 +496575 +4338 +552545 +5601 +75426 +568863 +184227 +170629 +438567 +505132 +541353 +284674 +322567 +182423 +312051 +18896 +40471 +321725 +188850 +37119 +95569 +187362 +397133 +528972 +487131 +174989 +370325 +223554 +385633 +103485 +537574 +63240 +256566 +86467 +401092 +486968 +308441 +280017 +527464 +131965 +310479 +125556 +220160 +532963 +310052 +107963 +293841 +388534 +45603 +368949 +391825 +5107 +569705 +231549 +250108 +152933 +206433 +358817 +434006 +283904 +152808 +539975 +24629 +410231 +13465 +502318 +51961 +445594 +209062 +38726 +295420 +430079 +240147 +561512 +35795 +102589 +505619 +565469 +271772 +520561 +372300 +178807 +492805 +1083 +303704 +125635 +217521 +278032 +208688 +335325 +140435 +313990 +143822 +320857 +549230 +76844 +424219 +463876 +243199 +2988 +215170 +30012 +377738 +408568 +490624 +404839 +138316 +157206 +404461 +122934 +263346 +21327 +99913 +67975 +339676 +391891 +365305 +337055 +233834 +125524 +46869 +32577 +304744 +104176 +167356 +210404 +307989 +217223 +196046 +454414 +16356 +244487 +543660 +197461 +199681 +476787 +455085 +307074 +260547 +107468 +334769 +29437 +166837 +53838 +502979 +82678 +288860 +535523 +311950 +237723 +98656 +223123 +273930 +58057 +544334 +324857 +198043 +535326 +316505 +12991 +576820 +43611 +107839 +275749 +456695 +78188 +375786 +466239 +184830 +537128 +434513 +244344 +374576 +69140 +434247 +555009 +510857 +220819 +20598 +99416 +74967 +533129 +515577 +213361 +330974 +548848 +431557 +503278 +130043 +402570 +320554 +559884 +252629 +364596 +423484 +271230 +105552 +143143 +285751 +49994 +204162 +80646 +381393 +123415 +118417 +30932 +425412 +388130 +551243 +468337 +484893 +25014 +174390 +463781 +124647 +60823 +361964 +425702 +575110 +532390 +230881 +84592 +189997 +221307 +361472 +32364 +71918 +316365 +492378 +234251 +48504 +418070 +89884 +562045 +506552 +66360 +122962 +262605 +529939 +345229 +294853 +344397 +56091 +8599 +459823 +175785 +226128 +259983 +354515 +379144 +384995 +205253 +116786 +441432 +448810 +83452 +465129 +506906 +90616 +551959 +406404 +157891 +362090 +439630 +45099 +61960 +478430 +489605 +127050 +579872 +475798 +64510 +447733 +33066 +102848 +538819 +323760 +200401 +179765 +251317 +239376 +83836 +578092 +522452 +393056 +278848 +27787 +377239 +473427 +83065 +377005 +576539 +248019 +473370 +536369 +92648 +332461 +437609 +274800 +388846 +323048 +193407 +541898 +480140 +46526 +26432 +339738 +325991 +37705 +528033 +542922 +313420 +190463 +531000 +454907 +26448 +238199 +476652 +457147 +364256 +72632 +430380 +315448 +353320 +18158 +91527 +454252 +546987 +386370 +38064 +19763 +64152 +453216 +55223 +361860 +522566 +509531 +438432 +31164 +163290 +389197 +333440 +173464 +447842 +381615 +99961 +156126 +103134 +394940 +165638 +261706 +378311 +534081 +373848 +401642 +338019 +378096 +289610 +547421 +174672 +133343 +191360 +293751 +520892 +145214 +167668 +37456 +460962 +465267 +292804 +347529 +203661 +10766 +27371 +203845 +155736 +136715 +463588 +26640 +547612 +131453 +184274 +442456 +265085 +223256 +129420 +23019 +536467 +194532 +127585 +392637 +330408 +524775 +31993 +433924 +502852 +553129 +559364 +297343 +71360 +225537 +271148 +345499 +475893 +237463 +5278 +501243 +413235 +444236 +541071 +380088 +468063 +94858 +225913 +295614 +210276 +170975 +205570 +422375 +550365 +308702 +484627 +565031 +98979 +480345 +579548 +272673 +436875 +287874 +16502 +274917 +281809 +442968 +289263 +347766 +160933 +84533 +266409 +122199 +396200 +30958 +504541 +1591 +89432 +387150 +306383 +15260 +154515 +50752 +166913 +102644 +100196 +160278 +349579 +442536 +17923 +310564 +62020 +152004 +578330 +126299 +527025 +83494 +226400 +268435 +445334 +310391 +505156 +19157 +44677 +318171 +447765 +354369 +527486 +329939 +184771 +134856 +467675 +517133 +89697 +447080 +70685 +144938 +519673 +485758 +454957 +564851 +189451 +408757 +192616 +280734 +305060 +243946 +99179 +303971 +170519 +48917 +549965 +300245 +384101 +576607 +186709 +516341 +241668 +133470 +134811 +500825 +464689 +29833 +343820 +213429 +387434 +279305 +444207 +210777 +372043 +189868 +572229 +8495 +370090 +450282 +277080 +199158 +109612 +567708 +245659 +485129 +268363 +23448 +5352 +235597 +6871 +348720 +94113 +314613 +63729 +114458 +215394 +460460 +240387 +398726 +135604 +571728 +415770 +286908 +138151 +146272 +344094 +345209 +241187 +282768 +113037 +545583 +219283 +145873 +285957 +489235 +157271 +197458 +502671 +499845 +334884 +79084 +505573 +115618 +561491 +354202 +279838 +190734 +134738 +269450 +482784 +144610 +52774 +290659 +440646 +25807 +442952 +159215 +318224 +73445 +211653 +527960 +401862 +431026 +488755 +292278 +400554 +272630 +382668 +470298 +166426 +129645 +28820 +161227 +417696 +560677 +283216 +28978 +310302 +154419 +230450 +328289 +73118 +104691 +15085 +405574 +510548 +470005 +102928 +569249 +413126 +77282 +96732 +359020 +42182 +250875 +106206 +354929 +320796 +453341 +237318 +254834 +137265 +399865 +292685 +152252 +319579 +81484 +16599 +162257 +351034 +396051 +502275 +308278 +34483 +13333 +320290 +321579 +349794 +99219 +200162 +369470 +487583 +62703 +251639 +138246 +157170 +477112 +283963 +74860 +307057 +364075 +295491 +34757 +400161 +170194 +120874 +492817 +3817 +183973 +135436 +512989 +114744 +379210 +201072 +293785 +578385 +237420 +7888 +18224 +155317 +522406 +441440 +110482 +173400 +183348 +552504 +475660 +166948 +147025 +443259 +578792 +245227 +546687 +474519 +393284 +249668 +87493 +151651 +100306 +540466 +546556 +212675 +282942 +21310 +385535 +7304 +303409 +386116 +574297 +514550 +217133 +533553 +447152 +578703 +45392 +166205 +180154 +25143 +338802 +330110 +261389 +343506 +442726 +285388 +554934 +421316 +479912 +85192 +34874 +487266 +226173 +20748 +360660 +574509 +543364 +1554 +125539 +566931 +312889 +466945 +444804 +257187 +568587 +427160 +71123 +563849 +138589 +162841 +129663 +107226 +140686 +321663 +437117 +179808 +321718 +62398 +16497 +468933 +219841 +355430 +293554 +293044 +109516 +485887 +490620 +579893 +427135 +31636 +217919 +432441 +314396 +119802 +393682 +201764 +146193 +116358 +84825 +208311 +419774 +177468 +72052 +142585 +519598 +464006 +556083 +412136 +169361 +442929 +84567 +549932 +75560 +74656 +93314 +393838 +383018 +372433 +431281 +556278 +5513 +108503 +500478 +148588 +138713 +368153 +22646 +303778 +270758 +276706 +275429 +492025 +169111 +494328 +35891 +70258 +400528 +165229 +460494 +269311 +307658 +98283 +369294 +319345 +414578 +541550 +425388 +129855 +99477 +383073 +387906 +293124 +155873 +549224 +266021 +52869 +1584 +421902 +498535 +277235 +153013 +452013 +553561 +138040 +20820 +58483 +423506 +569001 +325153 +383039 +213421 +38825 +453283 +384661 +127702 +238147 +104893 +577826 +64974 +240655 +459153 +145665 +49810 +65008 +545385 +125070 +46433 +143329 +429174 +52947 +321314 +253341 +157365 +453162 +111910 +339019 +239575 +362219 +80652 +247317 +460286 +365724 +160875 +372220 +483389 +572181 +146190 +580975 +54761 +348488 +416104 +468778 +18833 +251537 +234366 +510078 +14723 +338595 +153797 +513098 +467138 +404618 +261982 +545730 +135846 +108244 +562557 +180524 +227370 +341856 +131743 +255691 +497878 +68878 +430640 +441473 +347664 +214369 +347018 +225238 +421762 +317024 +6180 +172004 +303101 +22488 +193494 +199346 +409627 +315350 +263463 +190722 +523292 +363902 +573778 +437290 +389812 +517082 +145073 +37907 +489763 +456261 +270386 +508917 +566823 +543897 +362482 +130966 +66632 +181962 +274613 +135708 +549746 +323766 +366714 +353295 +318813 +153307 +213693 +293378 +149446 +199927 +580543 +331727 +238488 +472833 +308645 +424225 +228746 +110435 +495377 +240646 +274491 +130921 +140006 +4688 +115241 +76962 +66650 +47718 +224991 +434187 +272048 +11169 +158222 +154000 +507436 +443499 +109937 +309692 +534018 +22797 +163339 +168683 +210098 +246069 +137954 +143320 +262587 +414795 +226938 +536831 +128791 +459590 +50514 +30067 +317479 +378655 +229968 +522702 +11122 +515266 +136600 +224509 +149912 +97656 +120747 +349480 +155199 +528731 +523807 +168544 +325664 +229981 +434410 +431208 +508996 +63791 +89225 +513690 +136740 +224364 +515424 +508302 +418175 +465552 +439907 +272097 +451087 +396304 +342273 +52507 +300066 +380089 +326248 +167906 +37846 +262993 +60090 +499249 +90432 +74456 +264660 +325598 +480985 +245411 +425644 +224724 +475439 +246478 +487438 +563731 +441854 +522665 +245915 +85747 +315162 +108761 +407521 +388528 +389453 +298331 +447791 +368820 +440034 +305677 +122208 +182369 +543531 +151820 +63650 +457580 +563381 +320899 +14869 +137260 +61925 +376307 +80367 +269089 +203705 +274835 +267321 +418106 +471273 +74037 +227855 +519758 +89045 +321217 +324203 +479129 +503431 +368528 +527718 +278579 +13525 +291582 +301837 +31667 +68120 +14007 +114158 +124262 +33626 +53949 +187585 +192247 +208844 +212766 +318671 +575012 +439339 +364073 +419624 +178078 +427783 +302159 +339368 +190680 +23807 +288579 +312720 +15778 +553558 +571834 +574376 +122161 +493815 +472376 +483432 +149123 +51628 +264628 +26609 +23696 +485081 +441323 +451679 +42055 +378795 +86439 +366493 +520996 +332869 +18014 +554523 +83476 +6040 +421834 +424392 +308160 +335233 +249809 +349098 +358090 +187349 +61782 +35498 +386514 +207108 +578418 +84447 +104108 +126107 +211674 +111909 +490708 +477025 +206757 +556205 +142484 +454296 +464366 +358254 +215482 +468548 +82680 +100909 +405432 +85764 +94651 +63973 +8131 +288592 +257470 +47597 +321557 +34520 +134066 +246701 +317797 +282365 +78176 +29577 +311075 +331937 +190395 +5802 +245112 +111032 +140556 +199127 +376491 +305253 +300375 +545903 +357782 +377911 +74963 +329336 +25057 +3244 +252020 +293474 +171050 +239306 +189772 +238090 +160031 +36761 +445675 +252716 +152214 +239466 +55155 +479829 +420281 +445812 +118106 +434576 +451104 +316708 +438535 +300322 +167952 +390072 +487220 +20247 +9400 +43944 +35770 +487351 +425462 +212203 +9668 +8981 +574241 +332096 +535563 +192944 +498733 +276151 +550645 +507037 +9769 +404249 +236747 +376416 +306415 +45966 +191296 +576875 +493932 +225075 +536444 +79920 +561681 +60700 +99874 +219437 +509819 +466665 +579326 +428739 +394611 +263083 +379554 +279391 +178516 +133690 +77396 +300137 +6861 +435359 +314108 +444152 +500139 +92749 +89188 +300233 +414201 +443204 +211097 diff --git a/workspace/virtuallab/object_detection/data/oid_bbox_trainable_label_map.pbtxt b/workspace/virtuallab/object_detection/data/oid_bbox_trainable_label_map.pbtxt new file mode 100644 index 0000000..863e4f3 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/oid_bbox_trainable_label_map.pbtxt @@ -0,0 +1,2725 @@ +item { + name: "/m/01g317" + id: 1 + display_name: "Person" +} +item { + name: "/m/09j2d" + id: 2 + display_name: "Clothing" +} +item { + name: "/m/04yx4" + id: 3 + display_name: "Man" +} +item { + name: "/m/0dzct" + id: 4 + display_name: "Face" +} +item { + name: "/m/07j7r" + id: 5 + display_name: "Tree" +} +item { + name: "/m/05s2s" + id: 6 + display_name: "Plant" +} +item { + name: "/m/03bt1vf" + id: 7 + display_name: "Woman" +} +item { + name: "/m/07yv9" + id: 8 + display_name: "Vehicle" +} +item { + name: "/m/0cgh4" + id: 9 + display_name: "Building" +} +item { + name: "/m/01prls" + id: 10 + display_name: "Land vehicle" +} +item { + name: "/m/09j5n" + id: 11 + display_name: "Footwear" +} +item { + name: "/m/05r655" + id: 12 + display_name: "Girl" +} +item { + name: "/m/0jbk" + id: 13 + display_name: "Animal" +} +item { + name: "/m/0k4j" + id: 14 + display_name: "Car" +} +item { + name: "/m/02wbm" + id: 15 + display_name: "Food" +} +item { + name: "/m/083wq" + id: 16 + display_name: "Wheel" +} +item { + name: "/m/0c9ph5" + id: 17 + display_name: "Flower" +} +item { + name: "/m/0c_jw" + id: 18 + display_name: "Furniture" +} +item { + name: "/m/0d4v4" + id: 19 + display_name: "Window" +} +item { + name: "/m/03jm5" + id: 20 + display_name: "House" +} +item { + name: "/m/01bl7v" + id: 21 + display_name: "Boy" +} +item { + name: "/m/0463sg" + id: 22 + display_name: "Fashion accessory" +} +item { + name: "/m/04bcr3" + id: 23 + display_name: "Table" +} +item { + name: "/m/0jyfg" + id: 24 + display_name: "Glasses" +} +item { + name: "/m/01xyhv" + id: 25 + display_name: "Suit" +} +item { + name: "/m/08dz3q" + id: 26 + display_name: "Auto part" +} +item { + name: "/m/015p6" + id: 27 + display_name: "Bird" +} +item { + name: "/m/05y5lj" + id: 28 + display_name: "Sports equipment" +} +item { + name: "/m/01d40f" + id: 29 + display_name: "Dress" +} +item { + name: "/m/0bt9lr" + id: 30 + display_name: "Dog" +} +item { + name: "/m/01lrl" + id: 31 + display_name: "Carnivore" +} +item { + name: "/m/02p0tk3" + id: 32 + display_name: "Human body" +} +item { + name: "/m/0fly7" + id: 33 + display_name: "Jeans" +} +item { + name: "/m/04szw" + id: 34 + display_name: "Musical instrument" +} +item { + name: "/m/0271t" + id: 35 + display_name: "Drink" +} +item { + name: "/m/019jd" + id: 36 + display_name: "Boat" +} +item { + name: "/m/03q69" + id: 37 + display_name: "Hair" +} +item { + name: "/m/0h9mv" + id: 38 + display_name: "Tire" +} +item { + name: "/m/04hgtk" + id: 39 + display_name: "Head" +} +item { + name: "/m/01yrx" + id: 40 + display_name: "Cat" +} +item { + name: "/m/01rzcn" + id: 41 + display_name: "Watercraft" +} +item { + name: "/m/01mzpv" + id: 42 + display_name: "Chair" +} +item { + name: "/m/0199g" + id: 43 + display_name: "Bike" +} +item { + name: "/m/01fdzj" + id: 44 + display_name: "Tower" +} +item { + name: "/m/04rky" + id: 45 + display_name: "Mammal" +} +item { + name: "/m/079cl" + id: 46 + display_name: "Skyscraper" +} +item { + name: "/m/0dzf4" + id: 47 + display_name: "Arm" +} +item { + name: "/m/0138tl" + id: 48 + display_name: "Toy" +} +item { + name: "/m/06msq" + id: 49 + display_name: "Sculpture" +} +item { + name: "/m/03xxp" + id: 50 + display_name: "Invertebrate" +} +item { + name: "/m/0hg7b" + id: 51 + display_name: "Microphone" +} +item { + name: "/m/01n5jq" + id: 52 + display_name: "Poster" +} +item { + name: "/m/03vt0" + id: 53 + display_name: "Insect" +} +item { + name: "/m/0342h" + id: 54 + display_name: "Guitar" +} +item { + name: "/m/0k0pj" + id: 55 + display_name: "Nose" +} +item { + name: "/m/02dl1y" + id: 56 + display_name: "Hat" +} +item { + name: "/m/04brg2" + id: 57 + display_name: "Tableware" +} +item { + name: "/m/02dgv" + id: 58 + display_name: "Door" +} +item { + name: "/m/01bqk0" + id: 59 + display_name: "Bicycle wheel" +} +item { + name: "/m/017ftj" + id: 60 + display_name: "Sunglasses" +} +item { + name: "/m/052lwg6" + id: 61 + display_name: "Baked goods" +} +item { + name: "/m/014sv8" + id: 62 + display_name: "Eye" +} +item { + name: "/m/0270h" + id: 63 + display_name: "Dessert" +} +item { + name: "/m/0283dt1" + id: 64 + display_name: "Mouth" +} +item { + name: "/m/0k5j" + id: 65 + display_name: "Aircraft" +} +item { + name: "/m/0cmf2" + id: 66 + display_name: "Airplane" +} +item { + name: "/m/07jdr" + id: 67 + display_name: "Train" +} +item { + name: "/m/032b3c" + id: 68 + display_name: "Jacket" +} +item { + name: "/m/033rq4" + id: 69 + display_name: "Street light" +} +item { + name: "/m/0k65p" + id: 70 + display_name: "Hand" +} +item { + name: "/m/01ww8y" + id: 71 + display_name: "Snack" +} +item { + name: "/m/0zvk5" + id: 72 + display_name: "Helmet" +} +item { + name: "/m/07mhn" + id: 73 + display_name: "Trousers" +} +item { + name: "/m/04dr76w" + id: 74 + display_name: "Bottle" +} +item { + name: "/m/03fp41" + id: 75 + display_name: "Houseplant" +} +item { + name: "/m/03k3r" + id: 76 + display_name: "Horse" +} +item { + name: "/m/01y9k5" + id: 77 + display_name: "Desk" +} +item { + name: "/m/0cdl1" + id: 78 + display_name: "Palm tree" +} +item { + name: "/m/0f4s2w" + id: 79 + display_name: "Vegetable" +} +item { + name: "/m/02xwb" + id: 80 + display_name: "Fruit" +} +item { + name: "/m/035r7c" + id: 81 + display_name: "Leg" +} +item { + name: "/m/0bt_c3" + id: 82 + display_name: "Book" +} +item { + name: "/m/01_bhs" + id: 83 + display_name: "Fast food" +} +item { + name: "/m/01599" + id: 84 + display_name: "Beer" +} +item { + name: "/m/03120" + id: 85 + display_name: "Flag" +} +item { + name: "/m/026t6" + id: 86 + display_name: "Drum" +} +item { + name: "/m/01bjv" + id: 87 + display_name: "Bus" +} +item { + name: "/m/07r04" + id: 88 + display_name: "Truck" +} +item { + name: "/m/018xm" + id: 89 + display_name: "Ball" +} +item { + name: "/m/01rkbr" + id: 90 + display_name: "Tie" +} +item { + name: "/m/0fm3zh" + id: 91 + display_name: "Flowerpot" +} +item { + name: "/m/02_n6y" + id: 92 + display_name: "Goggles" +} +item { + name: "/m/04_sv" + id: 93 + display_name: "Motorcycle" +} +item { + name: "/m/06z37_" + id: 94 + display_name: "Picture frame" +} +item { + name: "/m/01bfm9" + id: 95 + display_name: "Shorts" +} +item { + name: "/m/0h8mhzd" + id: 96 + display_name: "Sports uniform" +} +item { + name: "/m/0d_2m" + id: 97 + display_name: "Moths and butterflies" +} +item { + name: "/m/0gjbg72" + id: 98 + display_name: "Shelf" +} +item { + name: "/m/01n4qj" + id: 99 + display_name: "Shirt" +} +item { + name: "/m/0ch_cf" + id: 100 + display_name: "Fish" +} +item { + name: "/m/06m11" + id: 101 + display_name: "Rose" +} +item { + name: "/m/01jfm_" + id: 102 + display_name: "Licence plate" +} +item { + name: "/m/02crq1" + id: 103 + display_name: "Couch" +} +item { + name: "/m/083kb" + id: 104 + display_name: "Weapon" +} +item { + name: "/m/01c648" + id: 105 + display_name: "Laptop" +} +item { + name: "/m/09tvcd" + id: 106 + display_name: "Wine glass" +} +item { + name: "/m/0h2r6" + id: 107 + display_name: "Van" +} +item { + name: "/m/081qc" + id: 108 + display_name: "Wine" +} +item { + name: "/m/09ddx" + id: 109 + display_name: "Duck" +} +item { + name: "/m/03p3bw" + id: 110 + display_name: "Bicycle helmet" +} +item { + name: "/m/0cyf8" + id: 111 + display_name: "Butterfly" +} +item { + name: "/m/0b_rs" + id: 112 + display_name: "Swimming pool" +} +item { + name: "/m/039xj_" + id: 113 + display_name: "Ear" +} +item { + name: "/m/021sj1" + id: 114 + display_name: "Office" +} +item { + name: "/m/0dv5r" + id: 115 + display_name: "Camera" +} +item { + name: "/m/01lynh" + id: 116 + display_name: "Stairs" +} +item { + name: "/m/06bt6" + id: 117 + display_name: "Reptile" +} +item { + name: "/m/01226z" + id: 118 + display_name: "Football" +} +item { + name: "/m/0fszt" + id: 119 + display_name: "Cake" +} +item { + name: "/m/050k8" + id: 120 + display_name: "Mobile phone" +} +item { + name: "/m/02wbtzl" + id: 121 + display_name: "Sun hat" +} +item { + name: "/m/02p5f1q" + id: 122 + display_name: "Coffee cup" +} +item { + name: "/m/025nd" + id: 123 + display_name: "Christmas tree" +} +item { + name: "/m/02522" + id: 124 + display_name: "Computer monitor" +} +item { + name: "/m/09ct_" + id: 125 + display_name: "Helicopter" +} +item { + name: "/m/0cvnqh" + id: 126 + display_name: "Bench" +} +item { + name: "/m/0d5gx" + id: 127 + display_name: "Castle" +} +item { + name: "/m/01xygc" + id: 128 + display_name: "Coat" +} +item { + name: "/m/04m6gz" + id: 129 + display_name: "Porch" +} +item { + name: "/m/01gkx_" + id: 130 + display_name: "Swimwear" +} +item { + name: "/m/01s105" + id: 131 + display_name: "Cabinetry" +} +item { + name: "/m/01j61q" + id: 132 + display_name: "Tent" +} +item { + name: "/m/0hnnb" + id: 133 + display_name: "Umbrella" +} +item { + name: "/m/01j51" + id: 134 + display_name: "Balloon" +} +item { + name: "/m/01knjb" + id: 135 + display_name: "Billboard" +} +item { + name: "/m/03__z0" + id: 136 + display_name: "Bookcase" +} +item { + name: "/m/01m2v" + id: 137 + display_name: "Computer keyboard" +} +item { + name: "/m/0167gd" + id: 138 + display_name: "Doll" +} +item { + name: "/m/0284d" + id: 139 + display_name: "Dairy" +} +item { + name: "/m/03ssj5" + id: 140 + display_name: "Bed" +} +item { + name: "/m/02fq_6" + id: 141 + display_name: "Fedora" +} +item { + name: "/m/06nwz" + id: 142 + display_name: "Seafood" +} +item { + name: "/m/0220r2" + id: 143 + display_name: "Fountain" +} +item { + name: "/m/01mqdt" + id: 144 + display_name: "Traffic sign" +} +item { + name: "/m/0268lbt" + id: 145 + display_name: "Hiking equipment" +} +item { + name: "/m/07c52" + id: 146 + display_name: "Television" +} +item { + name: "/m/0grw1" + id: 147 + display_name: "Salad" +} +item { + name: "/m/01h3n" + id: 148 + display_name: "Bee" +} +item { + name: "/m/078n6m" + id: 149 + display_name: "Coffee table" +} +item { + name: "/m/01xq0k1" + id: 150 + display_name: "Cattle" +} +item { + name: "/m/0gd2v" + id: 151 + display_name: "Marine mammal" +} +item { + name: "/m/0dbvp" + id: 152 + display_name: "Goose" +} +item { + name: "/m/03rszm" + id: 153 + display_name: "Curtain" +} +item { + name: "/m/0h8n5zk" + id: 154 + display_name: "Kitchen & dining room table" +} +item { + name: "/m/019dx1" + id: 155 + display_name: "Home appliance" +} +item { + name: "/m/03hl4l9" + id: 156 + display_name: "Marine invertebrates" +} +item { + name: "/m/0b3fp9" + id: 157 + display_name: "Countertop" +} +item { + name: "/m/02rdsp" + id: 158 + display_name: "Office supplies" +} +item { + name: "/m/0hf58v5" + id: 159 + display_name: "Luggage and bags" +} +item { + name: "/m/04h7h" + id: 160 + display_name: "Lighthouse" +} +item { + name: "/m/024g6" + id: 161 + display_name: "Cocktail" +} +item { + name: "/m/0cffdh" + id: 162 + display_name: "Maple" +} +item { + name: "/m/03q5c7" + id: 163 + display_name: "Saucer" +} +item { + name: "/m/014y4n" + id: 164 + display_name: "Paddle" +} +item { + name: "/m/01yx86" + id: 165 + display_name: "Bronze sculpture" +} +item { + name: "/m/020jm" + id: 166 + display_name: "Beetle" +} +item { + name: "/m/025dyy" + id: 167 + display_name: "Box" +} +item { + name: "/m/01llwg" + id: 168 + display_name: "Necklace" +} +item { + name: "/m/08pbxl" + id: 169 + display_name: "Monkey" +} +item { + name: "/m/02d9qx" + id: 170 + display_name: "Whiteboard" +} +item { + name: "/m/02pkr5" + id: 171 + display_name: "Plumbing fixture" +} +item { + name: "/m/0h99cwc" + id: 172 + display_name: "Kitchen appliance" +} +item { + name: "/m/050gv4" + id: 173 + display_name: "Plate" +} +item { + name: "/m/02vqfm" + id: 174 + display_name: "Coffee" +} +item { + name: "/m/09kx5" + id: 175 + display_name: "Deer" +} +item { + name: "/m/019w40" + id: 176 + display_name: "Surfboard" +} +item { + name: "/m/09dzg" + id: 177 + display_name: "Turtle" +} +item { + name: "/m/07k1x" + id: 178 + display_name: "Tool" +} +item { + name: "/m/080hkjn" + id: 179 + display_name: "Handbag" +} +item { + name: "/m/07qxg_" + id: 180 + display_name: "Football helmet" +} +item { + name: "/m/0ph39" + id: 181 + display_name: "Canoe" +} +item { + name: "/m/018p4k" + id: 182 + display_name: "Cart" +} +item { + name: "/m/02h19r" + id: 183 + display_name: "Scarf" +} +item { + name: "/m/015h_t" + id: 184 + display_name: "Beard" +} +item { + name: "/m/0fqfqc" + id: 185 + display_name: "Drawer" +} +item { + name: "/m/025rp__" + id: 186 + display_name: "Cowboy hat" +} +item { + name: "/m/01x3z" + id: 187 + display_name: "Clock" +} +item { + name: "/m/0crjs" + id: 188 + display_name: "Convenience store" +} +item { + name: "/m/0l515" + id: 189 + display_name: "Sandwich" +} +item { + name: "/m/015qff" + id: 190 + display_name: "Traffic light" +} +item { + name: "/m/09kmb" + id: 191 + display_name: "Spider" +} +item { + name: "/m/09728" + id: 192 + display_name: "Bread" +} +item { + name: "/m/071qp" + id: 193 + display_name: "Squirrel" +} +item { + name: "/m/02s195" + id: 194 + display_name: "Vase" +} +item { + name: "/m/06c54" + id: 195 + display_name: "Rifle" +} +item { + name: "/m/01xqw" + id: 196 + display_name: "Cello" +} +item { + name: "/m/05zsy" + id: 197 + display_name: "Pumpkin" +} +item { + name: "/m/0bwd_0j" + id: 198 + display_name: "Elephant" +} +item { + name: "/m/04m9y" + id: 199 + display_name: "Lizard" +} +item { + name: "/m/052sf" + id: 200 + display_name: "Mushroom" +} +item { + name: "/m/03grzl" + id: 201 + display_name: "Baseball glove" +} +item { + name: "/m/01z1kdw" + id: 202 + display_name: "Juice" +} +item { + name: "/m/02wv6h6" + id: 203 + display_name: "Skirt" +} +item { + name: "/m/016m2d" + id: 204 + display_name: "Skull" +} +item { + name: "/m/0dtln" + id: 205 + display_name: "Lamp" +} +item { + name: "/m/057cc" + id: 206 + display_name: "Musical keyboard" +} +item { + name: "/m/06k2mb" + id: 207 + display_name: "High heels" +} +item { + name: "/m/0f6wt" + id: 208 + display_name: "Falcon" +} +item { + name: "/m/0cxn2" + id: 209 + display_name: "Ice cream" +} +item { + name: "/m/02jvh9" + id: 210 + display_name: "Mug" +} +item { + name: "/m/0gjkl" + id: 211 + display_name: "Watch" +} +item { + name: "/m/01b638" + id: 212 + display_name: "Boot" +} +item { + name: "/m/071p9" + id: 213 + display_name: "Ski" +} +item { + name: "/m/0pg52" + id: 214 + display_name: "Taxi" +} +item { + name: "/m/0ftb8" + id: 215 + display_name: "Sunflower" +} +item { + name: "/m/0hnyx" + id: 216 + display_name: "Pastry" +} +item { + name: "/m/02jz0l" + id: 217 + display_name: "Tap" +} +item { + name: "/m/04kkgm" + id: 218 + display_name: "Bowl" +} +item { + name: "/m/0174n1" + id: 219 + display_name: "Glove" +} +item { + name: "/m/0gv1x" + id: 220 + display_name: "Parrot" +} +item { + name: "/m/09csl" + id: 221 + display_name: "Eagle" +} +item { + name: "/m/02jnhm" + id: 222 + display_name: "Tin can" +} +item { + name: "/m/099ssp" + id: 223 + display_name: "Platter" +} +item { + name: "/m/03nfch" + id: 224 + display_name: "Sandal" +} +item { + name: "/m/07y_7" + id: 225 + display_name: "Violin" +} +item { + name: "/m/05z6w" + id: 226 + display_name: "Penguin" +} +item { + name: "/m/03m3pdh" + id: 227 + display_name: "Sofa bed" +} +item { + name: "/m/09ld4" + id: 228 + display_name: "Frog" +} +item { + name: "/m/09b5t" + id: 229 + display_name: "Chicken" +} +item { + name: "/m/054xkw" + id: 230 + display_name: "Lifejacket" +} +item { + name: "/m/0130jx" + id: 231 + display_name: "Sink" +} +item { + name: "/m/07fbm7" + id: 232 + display_name: "Strawberry" +} +item { + name: "/m/01dws" + id: 233 + display_name: "Bear" +} +item { + name: "/m/01tcjp" + id: 234 + display_name: "Muffin" +} +item { + name: "/m/0dftk" + id: 235 + display_name: "Swan" +} +item { + name: "/m/0c06p" + id: 236 + display_name: "Candle" +} +item { + name: "/m/034c16" + id: 237 + display_name: "Pillow" +} +item { + name: "/m/09d5_" + id: 238 + display_name: "Owl" +} +item { + name: "/m/03hlz0c" + id: 239 + display_name: "Kitchen utensil" +} +item { + name: "/m/0ft9s" + id: 240 + display_name: "Dragonfly" +} +item { + name: "/m/011k07" + id: 241 + display_name: "Tortoise" +} +item { + name: "/m/054_l" + id: 242 + display_name: "Mirror" +} +item { + name: "/m/0jqgx" + id: 243 + display_name: "Lily" +} +item { + name: "/m/0663v" + id: 244 + display_name: "Pizza" +} +item { + name: "/m/0242l" + id: 245 + display_name: "Coin" +} +item { + name: "/m/014trl" + id: 246 + display_name: "Cosmetics" +} +item { + name: "/m/05r5c" + id: 247 + display_name: "Piano" +} +item { + name: "/m/07j87" + id: 248 + display_name: "Tomato" +} +item { + name: "/m/05kyg_" + id: 249 + display_name: "Chest of drawers" +} +item { + name: "/m/0kmg4" + id: 250 + display_name: "Teddy bear" +} +item { + name: "/m/07cmd" + id: 251 + display_name: "Tank" +} +item { + name: "/m/0dv77" + id: 252 + display_name: "Squash" +} +item { + name: "/m/096mb" + id: 253 + display_name: "Lion" +} +item { + name: "/m/01gmv2" + id: 254 + display_name: "Brassiere" +} +item { + name: "/m/07bgp" + id: 255 + display_name: "Sheep" +} +item { + name: "/m/0cmx8" + id: 256 + display_name: "Spoon" +} +item { + name: "/m/029tx" + id: 257 + display_name: "Dinosaur" +} +item { + name: "/m/073bxn" + id: 258 + display_name: "Tripod" +} +item { + name: "/m/0bh9flk" + id: 259 + display_name: "Tablet computer" +} +item { + name: "/m/06mf6" + id: 260 + display_name: "Rabbit" +} +item { + name: "/m/06_fw" + id: 261 + display_name: "Skateboard" +} +item { + name: "/m/078jl" + id: 262 + display_name: "Snake" +} +item { + name: "/m/0fbdv" + id: 263 + display_name: "Shellfish" +} +item { + name: "/m/0h23m" + id: 264 + display_name: "Sparrow" +} +item { + name: "/m/014j1m" + id: 265 + display_name: "Apple" +} +item { + name: "/m/03fwl" + id: 266 + display_name: "Goat" +} +item { + name: "/m/02y6n" + id: 267 + display_name: "French fries" +} +item { + name: "/m/06c7f7" + id: 268 + display_name: "Lipstick" +} +item { + name: "/m/026qbn5" + id: 269 + display_name: "studio couch" +} +item { + name: "/m/0cdn1" + id: 270 + display_name: "Hamburger" +} +item { + name: "/m/07clx" + id: 271 + display_name: "Tea" +} +item { + name: "/m/07cx4" + id: 272 + display_name: "Telephone" +} +item { + name: "/m/03g8mr" + id: 273 + display_name: "Baseball bat" +} +item { + name: "/m/0cnyhnx" + id: 274 + display_name: "Bull" +} +item { + name: "/m/01b7fy" + id: 275 + display_name: "Headphones" +} +item { + name: "/m/04gth" + id: 276 + display_name: "Lavender" +} +item { + name: "/m/0cyfs" + id: 277 + display_name: "Parachute" +} +item { + name: "/m/021mn" + id: 278 + display_name: "Cookie" +} +item { + name: "/m/07dm6" + id: 279 + display_name: "Tiger" +} +item { + name: "/m/0k1tl" + id: 280 + display_name: "Pen" +} +item { + name: "/m/0dv9c" + id: 281 + display_name: "Racket" +} +item { + name: "/m/0dt3t" + id: 282 + display_name: "Fork" +} +item { + name: "/m/04yqq2" + id: 283 + display_name: "Bust" +} +item { + name: "/m/01cmb2" + id: 284 + display_name: "Miniskirt" +} +item { + name: "/m/0gd36" + id: 285 + display_name: "Sea lion" +} +item { + name: "/m/033cnk" + id: 286 + display_name: "Egg" +} +item { + name: "/m/06ncr" + id: 287 + display_name: "Saxophone" +} +item { + name: "/m/03bk1" + id: 288 + display_name: "Giraffe" +} +item { + name: "/m/0bjyj5" + id: 289 + display_name: "Waste container" +} +item { + name: "/m/06__v" + id: 290 + display_name: "Snowboard" +} +item { + name: "/m/0qmmr" + id: 291 + display_name: "Wheelchair" +} +item { + name: "/m/01xgg_" + id: 292 + display_name: "Medical equipment" +} +item { + name: "/m/0czz2" + id: 293 + display_name: "Antelope" +} +item { + name: "/m/02l8p9" + id: 294 + display_name: "Harbor seal" +} +item { + name: "/m/09g1w" + id: 295 + display_name: "Toilet" +} +item { + name: "/m/0ll1f78" + id: 296 + display_name: "Shrimp" +} +item { + name: "/m/0cyhj_" + id: 297 + display_name: "Orange" +} +item { + name: "/m/0642b4" + id: 298 + display_name: "Cupboard" +} +item { + name: "/m/0h8mzrc" + id: 299 + display_name: "Wall clock" +} +item { + name: "/m/068zj" + id: 300 + display_name: "Pig" +} +item { + name: "/m/02z51p" + id: 301 + display_name: "Nightstand" +} +item { + name: "/m/0h8nr_l" + id: 302 + display_name: "Bathroom accessory" +} +item { + name: "/m/0388q" + id: 303 + display_name: "Grape" +} +item { + name: "/m/02hj4" + id: 304 + display_name: "Dolphin" +} +item { + name: "/m/01jfsr" + id: 305 + display_name: "Lantern" +} +item { + name: "/m/07gql" + id: 306 + display_name: "Trumpet" +} +item { + name: "/m/0h8my_4" + id: 307 + display_name: "Tennis racket" +} +item { + name: "/m/0n28_" + id: 308 + display_name: "Crab" +} +item { + name: "/m/0120dh" + id: 309 + display_name: "Sea turtle" +} +item { + name: "/m/020kz" + id: 310 + display_name: "Cannon" +} +item { + name: "/m/0mkg" + id: 311 + display_name: "Accordion" +} +item { + name: "/m/03c7gz" + id: 312 + display_name: "Door handle" +} +item { + name: "/m/09k_b" + id: 313 + display_name: "Lemon" +} +item { + name: "/m/031n1" + id: 314 + display_name: "Foot" +} +item { + name: "/m/04rmv" + id: 315 + display_name: "Mouse" +} +item { + name: "/m/084rd" + id: 316 + display_name: "Wok" +} +item { + name: "/m/02rgn06" + id: 317 + display_name: "Volleyball" +} +item { + name: "/m/05z55" + id: 318 + display_name: "Pasta" +} +item { + name: "/m/01r546" + id: 319 + display_name: "Earrings" +} +item { + name: "/m/09qck" + id: 320 + display_name: "Banana" +} +item { + name: "/m/012w5l" + id: 321 + display_name: "Ladder" +} +item { + name: "/m/01940j" + id: 322 + display_name: "Backpack" +} +item { + name: "/m/09f_2" + id: 323 + display_name: "Crocodile" +} +item { + name: "/m/02p3w7d" + id: 324 + display_name: "Roller skates" +} +item { + name: "/m/057p5t" + id: 325 + display_name: "Scoreboard" +} +item { + name: "/m/0d8zb" + id: 326 + display_name: "Jellyfish" +} +item { + name: "/m/01nq26" + id: 327 + display_name: "Sock" +} +item { + name: "/m/01x_v" + id: 328 + display_name: "Camel" +} +item { + name: "/m/05gqfk" + id: 329 + display_name: "Plastic bag" +} +item { + name: "/m/0cydv" + id: 330 + display_name: "Caterpillar" +} +item { + name: "/m/07030" + id: 331 + display_name: "Sushi" +} +item { + name: "/m/084zz" + id: 332 + display_name: "Whale" +} +item { + name: "/m/0c29q" + id: 333 + display_name: "Leopard" +} +item { + name: "/m/02zn6n" + id: 334 + display_name: "Barrel" +} +item { + name: "/m/03tw93" + id: 335 + display_name: "Fireplace" +} +item { + name: "/m/0fqt361" + id: 336 + display_name: "Stool" +} +item { + name: "/m/0f9_l" + id: 337 + display_name: "Snail" +} +item { + name: "/m/0gm28" + id: 338 + display_name: "Candy" +} +item { + name: "/m/09rvcxw" + id: 339 + display_name: "Rocket" +} +item { + name: "/m/01nkt" + id: 340 + display_name: "Cheese" +} +item { + name: "/m/04p0qw" + id: 341 + display_name: "Billiard table" +} +item { + name: "/m/03hj559" + id: 342 + display_name: "Mixing bowl" +} +item { + name: "/m/07pj7bq" + id: 343 + display_name: "Bowling equipment" +} +item { + name: "/m/04ctx" + id: 344 + display_name: "Knife" +} +item { + name: "/m/0703r8" + id: 345 + display_name: "Loveseat" +} +item { + name: "/m/03qrc" + id: 346 + display_name: "Hamster" +} +item { + name: "/m/020lf" + id: 347 + display_name: "Mouse" +} +item { + name: "/m/0by6g" + id: 348 + display_name: "Shark" +} +item { + name: "/m/01fh4r" + id: 349 + display_name: "Teapot" +} +item { + name: "/m/07c6l" + id: 350 + display_name: "Trombone" +} +item { + name: "/m/03bj1" + id: 351 + display_name: "Panda" +} +item { + name: "/m/0898b" + id: 352 + display_name: "Zebra" +} +item { + name: "/m/02x984l" + id: 353 + display_name: "Mechanical fan" +} +item { + name: "/m/0fj52s" + id: 354 + display_name: "Carrot" +} +item { + name: "/m/0cd4d" + id: 355 + display_name: "Cheetah" +} +item { + name: "/m/02068x" + id: 356 + display_name: "Gondola" +} +item { + name: "/m/01vbnl" + id: 357 + display_name: "Bidet" +} +item { + name: "/m/0449p" + id: 358 + display_name: "Jaguar" +} +item { + name: "/m/0gj37" + id: 359 + display_name: "Ladybug" +} +item { + name: "/m/0nl46" + id: 360 + display_name: "Crown" +} +item { + name: "/m/0152hh" + id: 361 + display_name: "Snowman" +} +item { + name: "/m/03dnzn" + id: 362 + display_name: "Bathtub" +} +item { + name: "/m/05_5p_0" + id: 363 + display_name: "Table tennis racket" +} +item { + name: "/m/02jfl0" + id: 364 + display_name: "Sombrero" +} +item { + name: "/m/01dxs" + id: 365 + display_name: "Brown bear" +} +item { + name: "/m/0cjq5" + id: 366 + display_name: "Lobster" +} +item { + name: "/m/040b_t" + id: 367 + display_name: "Refrigerator" +} +item { + name: "/m/0_cp5" + id: 368 + display_name: "Oyster" +} +item { + name: "/m/0gxl3" + id: 369 + display_name: "Handgun" +} +item { + name: "/m/029bxz" + id: 370 + display_name: "Oven" +} +item { + name: "/m/02zt3" + id: 371 + display_name: "Kite" +} +item { + name: "/m/03d443" + id: 372 + display_name: "Rhinoceros" +} +item { + name: "/m/0306r" + id: 373 + display_name: "Fox" +} +item { + name: "/m/0h8l4fh" + id: 374 + display_name: "Light bulb" +} +item { + name: "/m/0633h" + id: 375 + display_name: "Polar bear" +} +item { + name: "/m/01s55n" + id: 376 + display_name: "Suitcase" +} +item { + name: "/m/0hkxq" + id: 377 + display_name: "Broccoli" +} +item { + name: "/m/0cn6p" + id: 378 + display_name: "Otter" +} +item { + name: "/m/0dbzx" + id: 379 + display_name: "Mule" +} +item { + name: "/m/01dy8n" + id: 380 + display_name: "Woodpecker" +} +item { + name: "/m/01h8tj" + id: 381 + display_name: "Starfish" +} +item { + name: "/m/03s_tn" + id: 382 + display_name: "Kettle" +} +item { + name: "/m/01xs3r" + id: 383 + display_name: "Jet ski" +} +item { + name: "/m/031b6r" + id: 384 + display_name: "Window blind" +} +item { + name: "/m/06j2d" + id: 385 + display_name: "Raven" +} +item { + name: "/m/0hqkz" + id: 386 + display_name: "Grapefruit" +} +item { + name: "/m/01_5g" + id: 387 + display_name: "Chopsticks" +} +item { + name: "/m/02zvsm" + id: 388 + display_name: "Tart" +} +item { + name: "/m/0kpqd" + id: 389 + display_name: "Watermelon" +} +item { + name: "/m/015x4r" + id: 390 + display_name: "Cucumber" +} +item { + name: "/m/061hd_" + id: 391 + display_name: "Infant bed" +} +item { + name: "/m/04ylt" + id: 392 + display_name: "Missile" +} +item { + name: "/m/02wv84t" + id: 393 + display_name: "Gas stove" +} +item { + name: "/m/04y4h8h" + id: 394 + display_name: "Bathroom cabinet" +} +item { + name: "/m/01gllr" + id: 395 + display_name: "Beehive" +} +item { + name: "/m/0pcr" + id: 396 + display_name: "Alpaca" +} +item { + name: "/m/0jy4k" + id: 397 + display_name: "Doughnut" +} +item { + name: "/m/09f20" + id: 398 + display_name: "Hippopotamus" +} +item { + name: "/m/0mcx2" + id: 399 + display_name: "Ipod" +} +item { + name: "/m/04c0y" + id: 400 + display_name: "Kangaroo" +} +item { + name: "/m/0_k2" + id: 401 + display_name: "Ant" +} +item { + name: "/m/0jg57" + id: 402 + display_name: "Bell pepper" +} +item { + name: "/m/03fj2" + id: 403 + display_name: "Goldfish" +} +item { + name: "/m/03ldnb" + id: 404 + display_name: "Ceiling fan" +} +item { + name: "/m/06nrc" + id: 405 + display_name: "Shotgun" +} +item { + name: "/m/01btn" + id: 406 + display_name: "Barge" +} +item { + name: "/m/05vtc" + id: 407 + display_name: "Potato" +} +item { + name: "/m/08hvt4" + id: 408 + display_name: "Jug" +} +item { + name: "/m/0fx9l" + id: 409 + display_name: "Microwave oven" +} +item { + name: "/m/01h44" + id: 410 + display_name: "Bat" +} +item { + name: "/m/05n4y" + id: 411 + display_name: "Ostrich" +} +item { + name: "/m/0jly1" + id: 412 + display_name: "Turkey" +} +item { + name: "/m/06y5r" + id: 413 + display_name: "Sword" +} +item { + name: "/m/05ctyq" + id: 414 + display_name: "Tennis ball" +} +item { + name: "/m/0fp6w" + id: 415 + display_name: "Pineapple" +} +item { + name: "/m/0d4w1" + id: 416 + display_name: "Closet" +} +item { + name: "/m/02pv19" + id: 417 + display_name: "Stop sign" +} +item { + name: "/m/07crc" + id: 418 + display_name: "Taco" +} +item { + name: "/m/01dwwc" + id: 419 + display_name: "Pancake" +} +item { + name: "/m/01b9xk" + id: 420 + display_name: "Hot dog" +} +item { + name: "/m/013y1f" + id: 421 + display_name: "Organ" +} +item { + name: "/m/0m53l" + id: 422 + display_name: "Rays and skates" +} +item { + name: "/m/0174k2" + id: 423 + display_name: "Washing machine" +} +item { + name: "/m/01dwsz" + id: 424 + display_name: "Waffle" +} +item { + name: "/m/04vv5k" + id: 425 + display_name: "Snowplow" +} +item { + name: "/m/04cp_" + id: 426 + display_name: "Koala" +} +item { + name: "/m/0fz0h" + id: 427 + display_name: "Honeycomb" +} +item { + name: "/m/0llzx" + id: 428 + display_name: "Sewing machine" +} +item { + name: "/m/0319l" + id: 429 + display_name: "Horn" +} +item { + name: "/m/04v6l4" + id: 430 + display_name: "Frying pan" +} +item { + name: "/m/0dkzw" + id: 431 + display_name: "Seat belt" +} +item { + name: "/m/027pcv" + id: 432 + display_name: "Zucchini" +} +item { + name: "/m/0323sq" + id: 433 + display_name: "Golf cart" +} +item { + name: "/m/054fyh" + id: 434 + display_name: "Pitcher" +} +item { + name: "/m/01pns0" + id: 435 + display_name: "Fire hydrant" +} +item { + name: "/m/012n7d" + id: 436 + display_name: "Ambulance" +} +item { + name: "/m/044r5d" + id: 437 + display_name: "Golf ball" +} +item { + name: "/m/01krhy" + id: 438 + display_name: "Tiara" +} +item { + name: "/m/0dq75" + id: 439 + display_name: "Raccoon" +} +item { + name: "/m/0176mf" + id: 440 + display_name: "Belt" +} +item { + name: "/m/0h8lkj8" + id: 441 + display_name: "Corded phone" +} +item { + name: "/m/04tn4x" + id: 442 + display_name: "Swim cap" +} +item { + name: "/m/06l9r" + id: 443 + display_name: "Red panda" +} +item { + name: "/m/0cjs7" + id: 444 + display_name: "Asparagus" +} +item { + name: "/m/01lsmm" + id: 445 + display_name: "Scissors" +} +item { + name: "/m/01lcw4" + id: 446 + display_name: "Limousine" +} +item { + name: "/m/047j0r" + id: 447 + display_name: "Filing cabinet" +} +item { + name: "/m/01fb_0" + id: 448 + display_name: "Bagel" +} +item { + name: "/m/04169hn" + id: 449 + display_name: "Wood-burning stove" +} +item { + name: "/m/076bq" + id: 450 + display_name: "Segway" +} +item { + name: "/m/0hdln" + id: 451 + display_name: "Ruler" +} +item { + name: "/m/01g3x7" + id: 452 + display_name: "Bow and arrow" +} +item { + name: "/m/0l3ms" + id: 453 + display_name: "Balance beam" +} +item { + name: "/m/058qzx" + id: 454 + display_name: "Kitchen knife" +} +item { + name: "/m/0h8n6ft" + id: 455 + display_name: "Cake stand" +} +item { + name: "/m/018j2" + id: 456 + display_name: "Banjo" +} +item { + name: "/m/0l14j_" + id: 457 + display_name: "Flute" +} +item { + name: "/m/0wdt60w" + id: 458 + display_name: "Rugby ball" +} +item { + name: "/m/02gzp" + id: 459 + display_name: "Dagger" +} +item { + name: "/m/0h8n6f9" + id: 460 + display_name: "Dog bed" +} +item { + name: "/m/0fbw6" + id: 461 + display_name: "Cabbage" +} +item { + name: "/m/07kng9" + id: 462 + display_name: "Picnic basket" +} +item { + name: "/m/0dj6p" + id: 463 + display_name: "Peach" +} +item { + name: "/m/06pcq" + id: 464 + display_name: "Submarine sandwich" +} +item { + name: "/m/061_f" + id: 465 + display_name: "Pear" +} +item { + name: "/m/04g2r" + id: 466 + display_name: "Lynx" +} +item { + name: "/m/0jwn_" + id: 467 + display_name: "Pomegranate" +} +item { + name: "/m/02f9f_" + id: 468 + display_name: "Shower" +} +item { + name: "/m/01f8m5" + id: 469 + display_name: "Blue jay" +} +item { + name: "/m/01m4t" + id: 470 + display_name: "Printer" +} +item { + name: "/m/0cl4p" + id: 471 + display_name: "Hedgehog" +} +item { + name: "/m/07xyvk" + id: 472 + display_name: "Coffeemaker" +} +item { + name: "/m/084hf" + id: 473 + display_name: "Worm" +} +item { + name: "/m/03v5tg" + id: 474 + display_name: "Drinking straw" +} +item { + name: "/m/0qjjc" + id: 475 + display_name: "Remote control" +} +item { + name: "/m/015x5n" + id: 476 + display_name: "Radish" +} +item { + name: "/m/0ccs93" + id: 477 + display_name: "Canary" +} +item { + name: "/m/0nybt" + id: 478 + display_name: "Seahorse" +} +item { + name: "/m/02vkqh8" + id: 479 + display_name: "Wardrobe" +} +item { + name: "/m/09gtd" + id: 480 + display_name: "Toilet paper" +} +item { + name: "/m/019h78" + id: 481 + display_name: "Centipede" +} +item { + name: "/m/015wgc" + id: 482 + display_name: "Croissant" +} +item { + name: "/m/01x3jk" + id: 483 + display_name: "Snowmobile" +} +item { + name: "/m/01j3zr" + id: 484 + display_name: "Burrito" +} +item { + name: "/m/0c568" + id: 485 + display_name: "Porcupine" +} +item { + name: "/m/02pdsw" + id: 486 + display_name: "Cutting board" +} +item { + name: "/m/029b3" + id: 487 + display_name: "Dice" +} +item { + name: "/m/03q5t" + id: 488 + display_name: "Harpsichord" +} +item { + name: "/m/0p833" + id: 489 + display_name: "Perfume" +} +item { + name: "/m/01d380" + id: 490 + display_name: "Drill" +} +item { + name: "/m/024d2" + id: 491 + display_name: "Calculator" +} +item { + name: "/m/0mw_6" + id: 492 + display_name: "Willow" +} +item { + name: "/m/01f91_" + id: 493 + display_name: "Pretzel" +} +item { + name: "/m/02g30s" + id: 494 + display_name: "Guacamole" +} +item { + name: "/m/01hrv5" + id: 495 + display_name: "Popcorn" +} +item { + name: "/m/03m5k" + id: 496 + display_name: "Harp" +} +item { + name: "/m/0162_1" + id: 497 + display_name: "Towel" +} +item { + name: "/m/063rgb" + id: 498 + display_name: "Mixer" +} +item { + name: "/m/06_72j" + id: 499 + display_name: "Digital clock" +} +item { + name: "/m/046dlr" + id: 500 + display_name: "Alarm clock" +} +item { + name: "/m/047v4b" + id: 501 + display_name: "Artichoke" +} +item { + name: "/m/04zpv" + id: 502 + display_name: "Milk" +} +item { + name: "/m/043nyj" + id: 503 + display_name: "Common fig" +} +item { + name: "/m/03bbps" + id: 504 + display_name: "Power plugs and sockets" +} +item { + name: "/m/02w3r3" + id: 505 + display_name: "Paper towel" +} +item { + name: "/m/02pjr4" + id: 506 + display_name: "Blender" +} +item { + name: "/m/0755b" + id: 507 + display_name: "Scorpion" +} +item { + name: "/m/02lbcq" + id: 508 + display_name: "Stretcher" +} +item { + name: "/m/0fldg" + id: 509 + display_name: "Mango" +} +item { + name: "/m/012074" + id: 510 + display_name: "Magpie" +} +item { + name: "/m/035vxb" + id: 511 + display_name: "Isopod" +} +item { + name: "/m/02w3_ws" + id: 512 + display_name: "Personal care" +} +item { + name: "/m/0f6nr" + id: 513 + display_name: "Unicycle" +} +item { + name: "/m/0420v5" + id: 514 + display_name: "Punching bag" +} +item { + name: "/m/0frqm" + id: 515 + display_name: "Envelope" +} +item { + name: "/m/03txqz" + id: 516 + display_name: "Scale" +} +item { + name: "/m/0271qf7" + id: 517 + display_name: "Wine rack" +} +item { + name: "/m/074d1" + id: 518 + display_name: "Submarine" +} +item { + name: "/m/08p92x" + id: 519 + display_name: "Cream" +} +item { + name: "/m/01j4z9" + id: 520 + display_name: "Chainsaw" +} +item { + name: "/m/0kpt_" + id: 521 + display_name: "Cantaloupe" +} +item { + name: "/m/0h8n27j" + id: 522 + display_name: "Serving tray" +} +item { + name: "/m/03y6mg" + id: 523 + display_name: "Food processor" +} +item { + name: "/m/04h8sr" + id: 524 + display_name: "Dumbbell" +} +item { + name: "/m/065h6l" + id: 525 + display_name: "Jacuzzi" +} +item { + name: "/m/02tsc9" + id: 526 + display_name: "Slow cooker" +} +item { + name: "/m/012ysf" + id: 527 + display_name: "Syringe" +} +item { + name: "/m/0ky7b" + id: 528 + display_name: "Dishwasher" +} +item { + name: "/m/02wg_p" + id: 529 + display_name: "Tree house" +} +item { + name: "/m/0584n8" + id: 530 + display_name: "Briefcase" +} +item { + name: "/m/03kt2w" + id: 531 + display_name: "Stationary bicycle" +} +item { + name: "/m/05kms" + id: 532 + display_name: "Oboe" +} +item { + name: "/m/030610" + id: 533 + display_name: "Treadmill" +} +item { + name: "/m/0lt4_" + id: 534 + display_name: "Binoculars" +} +item { + name: "/m/076lb9" + id: 535 + display_name: "Bench" +} +item { + name: "/m/02ctlc" + id: 536 + display_name: "Cricket ball" +} +item { + name: "/m/02x8cch" + id: 537 + display_name: "Salt and pepper shakers" +} +item { + name: "/m/09gys" + id: 538 + display_name: "Squid" +} +item { + name: "/m/03jbxj" + id: 539 + display_name: "Light switch" +} +item { + name: "/m/012xff" + id: 540 + display_name: "Toothbrush" +} +item { + name: "/m/0h8kx63" + id: 541 + display_name: "Spice rack" +} +item { + name: "/m/073g6" + id: 542 + display_name: "Stethoscope" +} +item { + name: "/m/02cvgx" + id: 543 + display_name: "Winter melon" +} +item { + name: "/m/027rl48" + id: 544 + display_name: "Ladle" +} +item { + name: "/m/01kb5b" + id: 545 + display_name: "Flashlight" +} diff --git a/workspace/virtuallab/object_detection/data/oid_object_detection_challenge_500_label_map.pbtxt b/workspace/virtuallab/object_detection/data/oid_object_detection_challenge_500_label_map.pbtxt new file mode 100644 index 0000000..044f6d4 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/oid_object_detection_challenge_500_label_map.pbtxt @@ -0,0 +1,2500 @@ +item { + name: "/m/061hd_" + id: 1 + display_name: "Infant bed" +} +item { + name: "/m/06m11" + id: 2 + display_name: "Rose" +} +item { + name: "/m/03120" + id: 3 + display_name: "Flag" +} +item { + name: "/m/01kb5b" + id: 4 + display_name: "Flashlight" +} +item { + name: "/m/0120dh" + id: 5 + display_name: "Sea turtle" +} +item { + name: "/m/0dv5r" + id: 6 + display_name: "Camera" +} +item { + name: "/m/0jbk" + id: 7 + display_name: "Animal" +} +item { + name: "/m/0174n1" + id: 8 + display_name: "Glove" +} +item { + name: "/m/09f_2" + id: 9 + display_name: "Crocodile" +} +item { + name: "/m/01xq0k1" + id: 10 + display_name: "Cattle" +} +item { + name: "/m/03jm5" + id: 11 + display_name: "House" +} +item { + name: "/m/02g30s" + id: 12 + display_name: "Guacamole" +} +item { + name: "/m/05z6w" + id: 13 + display_name: "Penguin" +} +item { + name: "/m/01jfm_" + id: 14 + display_name: "Vehicle registration plate" +} +item { + name: "/m/076lb9" + id: 15 + display_name: "Training bench" +} +item { + name: "/m/0gj37" + id: 16 + display_name: "Ladybug" +} +item { + name: "/m/0k0pj" + id: 17 + display_name: "Human nose" +} +item { + name: "/m/0kpqd" + id: 18 + display_name: "Watermelon" +} +item { + name: "/m/0l14j_" + id: 19 + display_name: "Flute" +} +item { + name: "/m/0cyf8" + id: 20 + display_name: "Butterfly" +} +item { + name: "/m/0174k2" + id: 21 + display_name: "Washing machine" +} +item { + name: "/m/0dq75" + id: 22 + display_name: "Raccoon" +} +item { + name: "/m/076bq" + id: 23 + display_name: "Segway" +} +item { + name: "/m/07crc" + id: 24 + display_name: "Taco" +} +item { + name: "/m/0d8zb" + id: 25 + display_name: "Jellyfish" +} +item { + name: "/m/0fszt" + id: 26 + display_name: "Cake" +} +item { + name: "/m/0k1tl" + id: 27 + display_name: "Pen" +} +item { + name: "/m/020kz" + id: 28 + display_name: "Cannon" +} +item { + name: "/m/09728" + id: 29 + display_name: "Bread" +} +item { + name: "/m/07j7r" + id: 30 + display_name: "Tree" +} +item { + name: "/m/0fbdv" + id: 31 + display_name: "Shellfish" +} +item { + name: "/m/03ssj5" + id: 32 + display_name: "Bed" +} +item { + name: "/m/03qrc" + id: 33 + display_name: "Hamster" +} +item { + name: "/m/02dl1y" + id: 34 + display_name: "Hat" +} +item { + name: "/m/01k6s3" + id: 35 + display_name: "Toaster" +} +item { + name: "/m/02jfl0" + id: 36 + display_name: "Sombrero" +} +item { + name: "/m/01krhy" + id: 37 + display_name: "Tiara" +} +item { + name: "/m/04kkgm" + id: 38 + display_name: "Bowl" +} +item { + name: "/m/0ft9s" + id: 39 + display_name: "Dragonfly" +} +item { + name: "/m/0d_2m" + id: 40 + display_name: "Moths and butterflies" +} +item { + name: "/m/0czz2" + id: 41 + display_name: "Antelope" +} +item { + name: "/m/0f4s2w" + id: 42 + display_name: "Vegetable" +} +item { + name: "/m/07dd4" + id: 43 + display_name: "Torch" +} +item { + name: "/m/0cgh4" + id: 44 + display_name: "Building" +} +item { + name: "/m/03bbps" + id: 45 + display_name: "Power plugs and sockets" +} +item { + name: "/m/02pjr4" + id: 46 + display_name: "Blender" +} +item { + name: "/m/04p0qw" + id: 47 + display_name: "Billiard table" +} +item { + name: "/m/02pdsw" + id: 48 + display_name: "Cutting board" +} +item { + name: "/m/01yx86" + id: 49 + display_name: "Bronze sculpture" +} +item { + name: "/m/09dzg" + id: 50 + display_name: "Turtle" +} +item { + name: "/m/0hkxq" + id: 51 + display_name: "Broccoli" +} +item { + name: "/m/07dm6" + id: 52 + display_name: "Tiger" +} +item { + name: "/m/054_l" + id: 53 + display_name: "Mirror" +} +item { + name: "/m/01dws" + id: 54 + display_name: "Bear" +} +item { + name: "/m/027pcv" + id: 55 + display_name: "Zucchini" +} +item { + name: "/m/01d40f" + id: 56 + display_name: "Dress" +} +item { + name: "/m/02rgn06" + id: 57 + display_name: "Volleyball" +} +item { + name: "/m/0342h" + id: 58 + display_name: "Guitar" +} +item { + name: "/m/06bt6" + id: 59 + display_name: "Reptile" +} +item { + name: "/m/0323sq" + id: 60 + display_name: "Golf cart" +} +item { + name: "/m/02zvsm" + id: 61 + display_name: "Tart" +} +item { + name: "/m/02fq_6" + id: 62 + display_name: "Fedora" +} +item { + name: "/m/01lrl" + id: 63 + display_name: "Carnivore" +} +item { + name: "/m/0k4j" + id: 64 + display_name: "Car" +} +item { + name: "/m/04h7h" + id: 65 + display_name: "Lighthouse" +} +item { + name: "/m/07xyvk" + id: 66 + display_name: "Coffeemaker" +} +item { + name: "/m/03y6mg" + id: 67 + display_name: "Food processor" +} +item { + name: "/m/07r04" + id: 68 + display_name: "Truck" +} +item { + name: "/m/03__z0" + id: 69 + display_name: "Bookcase" +} +item { + name: "/m/019w40" + id: 70 + display_name: "Surfboard" +} +item { + name: "/m/09j5n" + id: 71 + display_name: "Footwear" +} +item { + name: "/m/0cvnqh" + id: 72 + display_name: "Bench" +} +item { + name: "/m/01llwg" + id: 73 + display_name: "Necklace" +} +item { + name: "/m/0c9ph5" + id: 74 + display_name: "Flower" +} +item { + name: "/m/015x5n" + id: 75 + display_name: "Radish" +} +item { + name: "/m/0gd2v" + id: 76 + display_name: "Marine mammal" +} +item { + name: "/m/04v6l4" + id: 77 + display_name: "Frying pan" +} +item { + name: "/m/02jz0l" + id: 78 + display_name: "Tap" +} +item { + name: "/m/0dj6p" + id: 79 + display_name: "Peach" +} +item { + name: "/m/04ctx" + id: 80 + display_name: "Knife" +} +item { + name: "/m/080hkjn" + id: 81 + display_name: "Handbag" +} +item { + name: "/m/01c648" + id: 82 + display_name: "Laptop" +} +item { + name: "/m/01j61q" + id: 83 + display_name: "Tent" +} +item { + name: "/m/012n7d" + id: 84 + display_name: "Ambulance" +} +item { + name: "/m/025nd" + id: 85 + display_name: "Christmas tree" +} +item { + name: "/m/09csl" + id: 86 + display_name: "Eagle" +} +item { + name: "/m/01lcw4" + id: 87 + display_name: "Limousine" +} +item { + name: "/m/0h8n5zk" + id: 88 + display_name: "Kitchen & dining room table" +} +item { + name: "/m/0633h" + id: 89 + display_name: "Polar bear" +} +item { + name: "/m/01fdzj" + id: 90 + display_name: "Tower" +} +item { + name: "/m/01226z" + id: 91 + display_name: "Football" +} +item { + name: "/m/0mw_6" + id: 92 + display_name: "Willow" +} +item { + name: "/m/04hgtk" + id: 93 + display_name: "Human head" +} +item { + name: "/m/02pv19" + id: 94 + display_name: "Stop sign" +} +item { + name: "/m/09qck" + id: 95 + display_name: "Banana" +} +item { + name: "/m/063rgb" + id: 96 + display_name: "Mixer" +} +item { + name: "/m/0lt4_" + id: 97 + display_name: "Binoculars" +} +item { + name: "/m/0270h" + id: 98 + display_name: "Dessert" +} +item { + name: "/m/01h3n" + id: 99 + display_name: "Bee" +} +item { + name: "/m/01mzpv" + id: 100 + display_name: "Chair" +} +item { + name: "/m/04169hn" + id: 101 + display_name: "Wood-burning stove" +} +item { + name: "/m/0fm3zh" + id: 102 + display_name: "Flowerpot" +} +item { + name: "/m/0d20w4" + id: 103 + display_name: "Beaker" +} +item { + name: "/m/0_cp5" + id: 104 + display_name: "Oyster" +} +item { + name: "/m/01dy8n" + id: 105 + display_name: "Woodpecker" +} +item { + name: "/m/03m5k" + id: 106 + display_name: "Harp" +} +item { + name: "/m/03dnzn" + id: 107 + display_name: "Bathtub" +} +item { + name: "/m/0h8mzrc" + id: 108 + display_name: "Wall clock" +} +item { + name: "/m/0h8mhzd" + id: 109 + display_name: "Sports uniform" +} +item { + name: "/m/03d443" + id: 110 + display_name: "Rhinoceros" +} +item { + name: "/m/01gllr" + id: 111 + display_name: "Beehive" +} +item { + name: "/m/0642b4" + id: 112 + display_name: "Cupboard" +} +item { + name: "/m/09b5t" + id: 113 + display_name: "Chicken" +} +item { + name: "/m/04yx4" + id: 114 + display_name: "Man" +} +item { + name: "/m/01f8m5" + id: 115 + display_name: "Blue jay" +} +item { + name: "/m/015x4r" + id: 116 + display_name: "Cucumber" +} +item { + name: "/m/01j51" + id: 117 + display_name: "Balloon" +} +item { + name: "/m/02zt3" + id: 118 + display_name: "Kite" +} +item { + name: "/m/03tw93" + id: 119 + display_name: "Fireplace" +} +item { + name: "/m/01jfsr" + id: 120 + display_name: "Lantern" +} +item { + name: "/m/04ylt" + id: 121 + display_name: "Missile" +} +item { + name: "/m/0bt_c3" + id: 122 + display_name: "Book" +} +item { + name: "/m/0cmx8" + id: 123 + display_name: "Spoon" +} +item { + name: "/m/0hqkz" + id: 124 + display_name: "Grapefruit" +} +item { + name: "/m/071qp" + id: 125 + display_name: "Squirrel" +} +item { + name: "/m/0cyhj_" + id: 126 + display_name: "Orange" +} +item { + name: "/m/01xygc" + id: 127 + display_name: "Coat" +} +item { + name: "/m/0420v5" + id: 128 + display_name: "Punching bag" +} +item { + name: "/m/0898b" + id: 129 + display_name: "Zebra" +} +item { + name: "/m/01knjb" + id: 130 + display_name: "Billboard" +} +item { + name: "/m/0199g" + id: 131 + display_name: "Bicycle" +} +item { + name: "/m/03c7gz" + id: 132 + display_name: "Door handle" +} +item { + name: "/m/02x984l" + id: 133 + display_name: "Mechanical fan" +} +item { + name: "/m/04zwwv" + id: 134 + display_name: "Ring binder" +} +item { + name: "/m/04bcr3" + id: 135 + display_name: "Table" +} +item { + name: "/m/0gv1x" + id: 136 + display_name: "Parrot" +} +item { + name: "/m/01nq26" + id: 137 + display_name: "Sock" +} +item { + name: "/m/02s195" + id: 138 + display_name: "Vase" +} +item { + name: "/m/083kb" + id: 139 + display_name: "Weapon" +} +item { + name: "/m/06nrc" + id: 140 + display_name: "Shotgun" +} +item { + name: "/m/0jyfg" + id: 141 + display_name: "Glasses" +} +item { + name: "/m/0nybt" + id: 142 + display_name: "Seahorse" +} +item { + name: "/m/0176mf" + id: 143 + display_name: "Belt" +} +item { + name: "/m/01rzcn" + id: 144 + display_name: "Watercraft" +} +item { + name: "/m/0d4v4" + id: 145 + display_name: "Window" +} +item { + name: "/m/03bk1" + id: 146 + display_name: "Giraffe" +} +item { + name: "/m/096mb" + id: 147 + display_name: "Lion" +} +item { + name: "/m/0h9mv" + id: 148 + display_name: "Tire" +} +item { + name: "/m/07yv9" + id: 149 + display_name: "Vehicle" +} +item { + name: "/m/0ph39" + id: 150 + display_name: "Canoe" +} +item { + name: "/m/01rkbr" + id: 151 + display_name: "Tie" +} +item { + name: "/m/0gjbg72" + id: 152 + display_name: "Shelf" +} +item { + name: "/m/06z37_" + id: 153 + display_name: "Picture frame" +} +item { + name: "/m/01m4t" + id: 154 + display_name: "Printer" +} +item { + name: "/m/035r7c" + id: 155 + display_name: "Human leg" +} +item { + name: "/m/019jd" + id: 156 + display_name: "Boat" +} +item { + name: "/m/02tsc9" + id: 157 + display_name: "Slow cooker" +} +item { + name: "/m/015wgc" + id: 158 + display_name: "Croissant" +} +item { + name: "/m/0c06p" + id: 159 + display_name: "Candle" +} +item { + name: "/m/01dwwc" + id: 160 + display_name: "Pancake" +} +item { + name: "/m/034c16" + id: 161 + display_name: "Pillow" +} +item { + name: "/m/0242l" + id: 162 + display_name: "Coin" +} +item { + name: "/m/02lbcq" + id: 163 + display_name: "Stretcher" +} +item { + name: "/m/03nfch" + id: 164 + display_name: "Sandal" +} +item { + name: "/m/03bt1vf" + id: 165 + display_name: "Woman" +} +item { + name: "/m/01lynh" + id: 166 + display_name: "Stairs" +} +item { + name: "/m/03q5t" + id: 167 + display_name: "Harpsichord" +} +item { + name: "/m/0fqt361" + id: 168 + display_name: "Stool" +} +item { + name: "/m/01bjv" + id: 169 + display_name: "Bus" +} +item { + name: "/m/01s55n" + id: 170 + display_name: "Suitcase" +} +item { + name: "/m/0283dt1" + id: 171 + display_name: "Human mouth" +} +item { + name: "/m/01z1kdw" + id: 172 + display_name: "Juice" +} +item { + name: "/m/016m2d" + id: 173 + display_name: "Skull" +} +item { + name: "/m/02dgv" + id: 174 + display_name: "Door" +} +item { + name: "/m/07y_7" + id: 175 + display_name: "Violin" +} +item { + name: "/m/01_5g" + id: 176 + display_name: "Chopsticks" +} +item { + name: "/m/06_72j" + id: 177 + display_name: "Digital clock" +} +item { + name: "/m/0ftb8" + id: 178 + display_name: "Sunflower" +} +item { + name: "/m/0c29q" + id: 179 + display_name: "Leopard" +} +item { + name: "/m/0jg57" + id: 180 + display_name: "Bell pepper" +} +item { + name: "/m/02l8p9" + id: 181 + display_name: "Harbor seal" +} +item { + name: "/m/078jl" + id: 182 + display_name: "Snake" +} +item { + name: "/m/0llzx" + id: 183 + display_name: "Sewing machine" +} +item { + name: "/m/0dbvp" + id: 184 + display_name: "Goose" +} +item { + name: "/m/09ct_" + id: 185 + display_name: "Helicopter" +} +item { + name: "/m/0dkzw" + id: 186 + display_name: "Seat belt" +} +item { + name: "/m/02p5f1q" + id: 187 + display_name: "Coffee cup" +} +item { + name: "/m/0fx9l" + id: 188 + display_name: "Microwave oven" +} +item { + name: "/m/01b9xk" + id: 189 + display_name: "Hot dog" +} +item { + name: "/m/0b3fp9" + id: 190 + display_name: "Countertop" +} +item { + name: "/m/0h8n27j" + id: 191 + display_name: "Serving tray" +} +item { + name: "/m/0h8n6f9" + id: 192 + display_name: "Dog bed" +} +item { + name: "/m/01599" + id: 193 + display_name: "Beer" +} +item { + name: "/m/017ftj" + id: 194 + display_name: "Sunglasses" +} +item { + name: "/m/044r5d" + id: 195 + display_name: "Golf ball" +} +item { + name: "/m/01dwsz" + id: 196 + display_name: "Waffle" +} +item { + name: "/m/0cdl1" + id: 197 + display_name: "Palm tree" +} +item { + name: "/m/07gql" + id: 198 + display_name: "Trumpet" +} +item { + name: "/m/0hdln" + id: 199 + display_name: "Ruler" +} +item { + name: "/m/0zvk5" + id: 200 + display_name: "Helmet" +} +item { + name: "/m/012w5l" + id: 201 + display_name: "Ladder" +} +item { + name: "/m/021sj1" + id: 202 + display_name: "Office building" +} +item { + name: "/m/0bh9flk" + id: 203 + display_name: "Tablet computer" +} +item { + name: "/m/09gtd" + id: 204 + display_name: "Toilet paper" +} +item { + name: "/m/0jwn_" + id: 205 + display_name: "Pomegranate" +} +item { + name: "/m/02wv6h6" + id: 206 + display_name: "Skirt" +} +item { + name: "/m/02wv84t" + id: 207 + display_name: "Gas stove" +} +item { + name: "/m/021mn" + id: 208 + display_name: "Cookie" +} +item { + name: "/m/018p4k" + id: 209 + display_name: "Cart" +} +item { + name: "/m/06j2d" + id: 210 + display_name: "Raven" +} +item { + name: "/m/033cnk" + id: 211 + display_name: "Egg" +} +item { + name: "/m/01j3zr" + id: 212 + display_name: "Burrito" +} +item { + name: "/m/03fwl" + id: 213 + display_name: "Goat" +} +item { + name: "/m/058qzx" + id: 214 + display_name: "Kitchen knife" +} +item { + name: "/m/06_fw" + id: 215 + display_name: "Skateboard" +} +item { + name: "/m/02x8cch" + id: 216 + display_name: "Salt and pepper shakers" +} +item { + name: "/m/04g2r" + id: 217 + display_name: "Lynx" +} +item { + name: "/m/01b638" + id: 218 + display_name: "Boot" +} +item { + name: "/m/099ssp" + id: 219 + display_name: "Platter" +} +item { + name: "/m/071p9" + id: 220 + display_name: "Ski" +} +item { + name: "/m/01gkx_" + id: 221 + display_name: "Swimwear" +} +item { + name: "/m/0b_rs" + id: 222 + display_name: "Swimming pool" +} +item { + name: "/m/03v5tg" + id: 223 + display_name: "Drinking straw" +} +item { + name: "/m/01j5ks" + id: 224 + display_name: "Wrench" +} +item { + name: "/m/026t6" + id: 225 + display_name: "Drum" +} +item { + name: "/m/0_k2" + id: 226 + display_name: "Ant" +} +item { + name: "/m/039xj_" + id: 227 + display_name: "Human ear" +} +item { + name: "/m/01b7fy" + id: 228 + display_name: "Headphones" +} +item { + name: "/m/0220r2" + id: 229 + display_name: "Fountain" +} +item { + name: "/m/015p6" + id: 230 + display_name: "Bird" +} +item { + name: "/m/0fly7" + id: 231 + display_name: "Jeans" +} +item { + name: "/m/07c52" + id: 232 + display_name: "Television" +} +item { + name: "/m/0n28_" + id: 233 + display_name: "Crab" +} +item { + name: "/m/0hg7b" + id: 234 + display_name: "Microphone" +} +item { + name: "/m/019dx1" + id: 235 + display_name: "Home appliance" +} +item { + name: "/m/04vv5k" + id: 236 + display_name: "Snowplow" +} +item { + name: "/m/020jm" + id: 237 + display_name: "Beetle" +} +item { + name: "/m/047v4b" + id: 238 + display_name: "Artichoke" +} +item { + name: "/m/01xs3r" + id: 239 + display_name: "Jet ski" +} +item { + name: "/m/03kt2w" + id: 240 + display_name: "Stationary bicycle" +} +item { + name: "/m/03q69" + id: 241 + display_name: "Human hair" +} +item { + name: "/m/01dxs" + id: 242 + display_name: "Brown bear" +} +item { + name: "/m/01h8tj" + id: 243 + display_name: "Starfish" +} +item { + name: "/m/0dt3t" + id: 244 + display_name: "Fork" +} +item { + name: "/m/0cjq5" + id: 245 + display_name: "Lobster" +} +item { + name: "/m/0h8lkj8" + id: 246 + display_name: "Corded phone" +} +item { + name: "/m/0271t" + id: 247 + display_name: "Drink" +} +item { + name: "/m/03q5c7" + id: 248 + display_name: "Saucer" +} +item { + name: "/m/0fj52s" + id: 249 + display_name: "Carrot" +} +item { + name: "/m/03vt0" + id: 250 + display_name: "Insect" +} +item { + name: "/m/01x3z" + id: 251 + display_name: "Clock" +} +item { + name: "/m/0d5gx" + id: 252 + display_name: "Castle" +} +item { + name: "/m/0h8my_4" + id: 253 + display_name: "Tennis racket" +} +item { + name: "/m/03ldnb" + id: 254 + display_name: "Ceiling fan" +} +item { + name: "/m/0cjs7" + id: 255 + display_name: "Asparagus" +} +item { + name: "/m/0449p" + id: 256 + display_name: "Jaguar" +} +item { + name: "/m/04szw" + id: 257 + display_name: "Musical instrument" +} +item { + name: "/m/07jdr" + id: 258 + display_name: "Train" +} +item { + name: "/m/01yrx" + id: 259 + display_name: "Cat" +} +item { + name: "/m/06c54" + id: 260 + display_name: "Rifle" +} +item { + name: "/m/04h8sr" + id: 261 + display_name: "Dumbbell" +} +item { + name: "/m/050k8" + id: 262 + display_name: "Mobile phone" +} +item { + name: "/m/0pg52" + id: 263 + display_name: "Taxi" +} +item { + name: "/m/02f9f_" + id: 264 + display_name: "Shower" +} +item { + name: "/m/054fyh" + id: 265 + display_name: "Pitcher" +} +item { + name: "/m/09k_b" + id: 266 + display_name: "Lemon" +} +item { + name: "/m/03xxp" + id: 267 + display_name: "Invertebrate" +} +item { + name: "/m/0jly1" + id: 268 + display_name: "Turkey" +} +item { + name: "/m/06k2mb" + id: 269 + display_name: "High heels" +} +item { + name: "/m/04yqq2" + id: 270 + display_name: "Bust" +} +item { + name: "/m/0bwd_0j" + id: 271 + display_name: "Elephant" +} +item { + name: "/m/02h19r" + id: 272 + display_name: "Scarf" +} +item { + name: "/m/02zn6n" + id: 273 + display_name: "Barrel" +} +item { + name: "/m/07c6l" + id: 274 + display_name: "Trombone" +} +item { + name: "/m/05zsy" + id: 275 + display_name: "Pumpkin" +} +item { + name: "/m/025dyy" + id: 276 + display_name: "Box" +} +item { + name: "/m/07j87" + id: 277 + display_name: "Tomato" +} +item { + name: "/m/09ld4" + id: 278 + display_name: "Frog" +} +item { + name: "/m/01vbnl" + id: 279 + display_name: "Bidet" +} +item { + name: "/m/0dzct" + id: 280 + display_name: "Human face" +} +item { + name: "/m/03fp41" + id: 281 + display_name: "Houseplant" +} +item { + name: "/m/0h2r6" + id: 282 + display_name: "Van" +} +item { + name: "/m/0by6g" + id: 283 + display_name: "Shark" +} +item { + name: "/m/0cxn2" + id: 284 + display_name: "Ice cream" +} +item { + name: "/m/04tn4x" + id: 285 + display_name: "Swim cap" +} +item { + name: "/m/0f6wt" + id: 286 + display_name: "Falcon" +} +item { + name: "/m/05n4y" + id: 287 + display_name: "Ostrich" +} +item { + name: "/m/0gxl3" + id: 288 + display_name: "Handgun" +} +item { + name: "/m/02d9qx" + id: 289 + display_name: "Whiteboard" +} +item { + name: "/m/04m9y" + id: 290 + display_name: "Lizard" +} +item { + name: "/m/05z55" + id: 291 + display_name: "Pasta" +} +item { + name: "/m/01x3jk" + id: 292 + display_name: "Snowmobile" +} +item { + name: "/m/0h8l4fh" + id: 293 + display_name: "Light bulb" +} +item { + name: "/m/031b6r" + id: 294 + display_name: "Window blind" +} +item { + name: "/m/01tcjp" + id: 295 + display_name: "Muffin" +} +item { + name: "/m/01f91_" + id: 296 + display_name: "Pretzel" +} +item { + name: "/m/02522" + id: 297 + display_name: "Computer monitor" +} +item { + name: "/m/0319l" + id: 298 + display_name: "Horn" +} +item { + name: "/m/0c_jw" + id: 299 + display_name: "Furniture" +} +item { + name: "/m/0l515" + id: 300 + display_name: "Sandwich" +} +item { + name: "/m/0306r" + id: 301 + display_name: "Fox" +} +item { + name: "/m/0crjs" + id: 302 + display_name: "Convenience store" +} +item { + name: "/m/0ch_cf" + id: 303 + display_name: "Fish" +} +item { + name: "/m/02xwb" + id: 304 + display_name: "Fruit" +} +item { + name: "/m/01r546" + id: 305 + display_name: "Earrings" +} +item { + name: "/m/03rszm" + id: 306 + display_name: "Curtain" +} +item { + name: "/m/0388q" + id: 307 + display_name: "Grape" +} +item { + name: "/m/03m3pdh" + id: 308 + display_name: "Sofa bed" +} +item { + name: "/m/03k3r" + id: 309 + display_name: "Horse" +} +item { + name: "/m/0hf58v5" + id: 310 + display_name: "Luggage and bags" +} +item { + name: "/m/01y9k5" + id: 311 + display_name: "Desk" +} +item { + name: "/m/05441v" + id: 312 + display_name: "Crutch" +} +item { + name: "/m/03p3bw" + id: 313 + display_name: "Bicycle helmet" +} +item { + name: "/m/0175cv" + id: 314 + display_name: "Tick" +} +item { + name: "/m/0cmf2" + id: 315 + display_name: "Airplane" +} +item { + name: "/m/0ccs93" + id: 316 + display_name: "Canary" +} +item { + name: "/m/02d1br" + id: 317 + display_name: "Spatula" +} +item { + name: "/m/0gjkl" + id: 318 + display_name: "Watch" +} +item { + name: "/m/0jqgx" + id: 319 + display_name: "Lily" +} +item { + name: "/m/0h99cwc" + id: 320 + display_name: "Kitchen appliance" +} +item { + name: "/m/047j0r" + id: 321 + display_name: "Filing cabinet" +} +item { + name: "/m/0k5j" + id: 322 + display_name: "Aircraft" +} +item { + name: "/m/0h8n6ft" + id: 323 + display_name: "Cake stand" +} +item { + name: "/m/0gm28" + id: 324 + display_name: "Candy" +} +item { + name: "/m/0130jx" + id: 325 + display_name: "Sink" +} +item { + name: "/m/04rmv" + id: 326 + display_name: "Mouse" +} +item { + name: "/m/081qc" + id: 327 + display_name: "Wine" +} +item { + name: "/m/0qmmr" + id: 328 + display_name: "Wheelchair" +} +item { + name: "/m/03fj2" + id: 329 + display_name: "Goldfish" +} +item { + name: "/m/040b_t" + id: 330 + display_name: "Refrigerator" +} +item { + name: "/m/02y6n" + id: 331 + display_name: "French fries" +} +item { + name: "/m/0fqfqc" + id: 332 + display_name: "Drawer" +} +item { + name: "/m/030610" + id: 333 + display_name: "Treadmill" +} +item { + name: "/m/07kng9" + id: 334 + display_name: "Picnic basket" +} +item { + name: "/m/029b3" + id: 335 + display_name: "Dice" +} +item { + name: "/m/0fbw6" + id: 336 + display_name: "Cabbage" +} +item { + name: "/m/07qxg_" + id: 337 + display_name: "Football helmet" +} +item { + name: "/m/068zj" + id: 338 + display_name: "Pig" +} +item { + name: "/m/01g317" + id: 339 + display_name: "Person" +} +item { + name: "/m/01bfm9" + id: 340 + display_name: "Shorts" +} +item { + name: "/m/02068x" + id: 341 + display_name: "Gondola" +} +item { + name: "/m/0fz0h" + id: 342 + display_name: "Honeycomb" +} +item { + name: "/m/0jy4k" + id: 343 + display_name: "Doughnut" +} +item { + name: "/m/05kyg_" + id: 344 + display_name: "Chest of drawers" +} +item { + name: "/m/01prls" + id: 345 + display_name: "Land vehicle" +} +item { + name: "/m/01h44" + id: 346 + display_name: "Bat" +} +item { + name: "/m/08pbxl" + id: 347 + display_name: "Monkey" +} +item { + name: "/m/02gzp" + id: 348 + display_name: "Dagger" +} +item { + name: "/m/04brg2" + id: 349 + display_name: "Tableware" +} +item { + name: "/m/031n1" + id: 350 + display_name: "Human foot" +} +item { + name: "/m/02jvh9" + id: 351 + display_name: "Mug" +} +item { + name: "/m/046dlr" + id: 352 + display_name: "Alarm clock" +} +item { + name: "/m/0h8ntjv" + id: 353 + display_name: "Pressure cooker" +} +item { + name: "/m/0k65p" + id: 354 + display_name: "Human hand" +} +item { + name: "/m/011k07" + id: 355 + display_name: "Tortoise" +} +item { + name: "/m/03grzl" + id: 356 + display_name: "Baseball glove" +} +item { + name: "/m/06y5r" + id: 357 + display_name: "Sword" +} +item { + name: "/m/061_f" + id: 358 + display_name: "Pear" +} +item { + name: "/m/01cmb2" + id: 359 + display_name: "Miniskirt" +} +item { + name: "/m/01mqdt" + id: 360 + display_name: "Traffic sign" +} +item { + name: "/m/05r655" + id: 361 + display_name: "Girl" +} +item { + name: "/m/02p3w7d" + id: 362 + display_name: "Roller skates" +} +item { + name: "/m/029tx" + id: 363 + display_name: "Dinosaur" +} +item { + name: "/m/04m6gz" + id: 364 + display_name: "Porch" +} +item { + name: "/m/015h_t" + id: 365 + display_name: "Human beard" +} +item { + name: "/m/06pcq" + id: 366 + display_name: "Submarine sandwich" +} +item { + name: "/m/01bms0" + id: 367 + display_name: "Screwdriver" +} +item { + name: "/m/07fbm7" + id: 368 + display_name: "Strawberry" +} +item { + name: "/m/09tvcd" + id: 369 + display_name: "Wine glass" +} +item { + name: "/m/06nwz" + id: 370 + display_name: "Seafood" +} +item { + name: "/m/0dv9c" + id: 371 + display_name: "Racket" +} +item { + name: "/m/083wq" + id: 372 + display_name: "Wheel" +} +item { + name: "/m/0gd36" + id: 373 + display_name: "Sea lion" +} +item { + name: "/m/0138tl" + id: 374 + display_name: "Toy" +} +item { + name: "/m/07clx" + id: 375 + display_name: "Tea" +} +item { + name: "/m/05ctyq" + id: 376 + display_name: "Tennis ball" +} +item { + name: "/m/0bjyj5" + id: 377 + display_name: "Waste container" +} +item { + name: "/m/0dbzx" + id: 378 + display_name: "Mule" +} +item { + name: "/m/02ctlc" + id: 379 + display_name: "Cricket ball" +} +item { + name: "/m/0fp6w" + id: 380 + display_name: "Pineapple" +} +item { + name: "/m/0djtd" + id: 381 + display_name: "Coconut" +} +item { + name: "/m/0167gd" + id: 382 + display_name: "Doll" +} +item { + name: "/m/078n6m" + id: 383 + display_name: "Coffee table" +} +item { + name: "/m/0152hh" + id: 384 + display_name: "Snowman" +} +item { + name: "/m/04gth" + id: 385 + display_name: "Lavender" +} +item { + name: "/m/0ll1f78" + id: 386 + display_name: "Shrimp" +} +item { + name: "/m/0cffdh" + id: 387 + display_name: "Maple" +} +item { + name: "/m/025rp__" + id: 388 + display_name: "Cowboy hat" +} +item { + name: "/m/02_n6y" + id: 389 + display_name: "Goggles" +} +item { + name: "/m/0wdt60w" + id: 390 + display_name: "Rugby ball" +} +item { + name: "/m/0cydv" + id: 391 + display_name: "Caterpillar" +} +item { + name: "/m/01n5jq" + id: 392 + display_name: "Poster" +} +item { + name: "/m/09rvcxw" + id: 393 + display_name: "Rocket" +} +item { + name: "/m/013y1f" + id: 394 + display_name: "Organ" +} +item { + name: "/m/06ncr" + id: 395 + display_name: "Saxophone" +} +item { + name: "/m/015qff" + id: 396 + display_name: "Traffic light" +} +item { + name: "/m/024g6" + id: 397 + display_name: "Cocktail" +} +item { + name: "/m/05gqfk" + id: 398 + display_name: "Plastic bag" +} +item { + name: "/m/0dv77" + id: 399 + display_name: "Squash" +} +item { + name: "/m/052sf" + id: 400 + display_name: "Mushroom" +} +item { + name: "/m/0cdn1" + id: 401 + display_name: "Hamburger" +} +item { + name: "/m/03jbxj" + id: 402 + display_name: "Light switch" +} +item { + name: "/m/0cyfs" + id: 403 + display_name: "Parachute" +} +item { + name: "/m/0kmg4" + id: 404 + display_name: "Teddy bear" +} +item { + name: "/m/02cvgx" + id: 405 + display_name: "Winter melon" +} +item { + name: "/m/09kx5" + id: 406 + display_name: "Deer" +} +item { + name: "/m/057cc" + id: 407 + display_name: "Musical keyboard" +} +item { + name: "/m/02pkr5" + id: 408 + display_name: "Plumbing fixture" +} +item { + name: "/m/057p5t" + id: 409 + display_name: "Scoreboard" +} +item { + name: "/m/03g8mr" + id: 410 + display_name: "Baseball bat" +} +item { + name: "/m/0frqm" + id: 411 + display_name: "Envelope" +} +item { + name: "/m/03m3vtv" + id: 412 + display_name: "Adhesive tape" +} +item { + name: "/m/0584n8" + id: 413 + display_name: "Briefcase" +} +item { + name: "/m/014y4n" + id: 414 + display_name: "Paddle" +} +item { + name: "/m/01g3x7" + id: 415 + display_name: "Bow and arrow" +} +item { + name: "/m/07cx4" + id: 416 + display_name: "Telephone" +} +item { + name: "/m/07bgp" + id: 417 + display_name: "Sheep" +} +item { + name: "/m/032b3c" + id: 418 + display_name: "Jacket" +} +item { + name: "/m/01bl7v" + id: 419 + display_name: "Boy" +} +item { + name: "/m/0663v" + id: 420 + display_name: "Pizza" +} +item { + name: "/m/0cn6p" + id: 421 + display_name: "Otter" +} +item { + name: "/m/02rdsp" + id: 422 + display_name: "Office supplies" +} +item { + name: "/m/02crq1" + id: 423 + display_name: "Couch" +} +item { + name: "/m/01xqw" + id: 424 + display_name: "Cello" +} +item { + name: "/m/0cnyhnx" + id: 425 + display_name: "Bull" +} +item { + name: "/m/01x_v" + id: 426 + display_name: "Camel" +} +item { + name: "/m/018xm" + id: 427 + display_name: "Ball" +} +item { + name: "/m/09ddx" + id: 428 + display_name: "Duck" +} +item { + name: "/m/084zz" + id: 429 + display_name: "Whale" +} +item { + name: "/m/01n4qj" + id: 430 + display_name: "Shirt" +} +item { + name: "/m/07cmd" + id: 431 + display_name: "Tank" +} +item { + name: "/m/04_sv" + id: 432 + display_name: "Motorcycle" +} +item { + name: "/m/0mkg" + id: 433 + display_name: "Accordion" +} +item { + name: "/m/09d5_" + id: 434 + display_name: "Owl" +} +item { + name: "/m/0c568" + id: 435 + display_name: "Porcupine" +} +item { + name: "/m/02wbtzl" + id: 436 + display_name: "Sun hat" +} +item { + name: "/m/05bm6" + id: 437 + display_name: "Nail" +} +item { + name: "/m/01lsmm" + id: 438 + display_name: "Scissors" +} +item { + name: "/m/0dftk" + id: 439 + display_name: "Swan" +} +item { + name: "/m/0dtln" + id: 440 + display_name: "Lamp" +} +item { + name: "/m/0nl46" + id: 441 + display_name: "Crown" +} +item { + name: "/m/05r5c" + id: 442 + display_name: "Piano" +} +item { + name: "/m/06msq" + id: 443 + display_name: "Sculpture" +} +item { + name: "/m/0cd4d" + id: 444 + display_name: "Cheetah" +} +item { + name: "/m/05kms" + id: 445 + display_name: "Oboe" +} +item { + name: "/m/02jnhm" + id: 446 + display_name: "Tin can" +} +item { + name: "/m/0fldg" + id: 447 + display_name: "Mango" +} +item { + name: "/m/073bxn" + id: 448 + display_name: "Tripod" +} +item { + name: "/m/029bxz" + id: 449 + display_name: "Oven" +} +item { + name: "/m/020lf" + id: 450 + display_name: "Computer mouse" +} +item { + name: "/m/01btn" + id: 451 + display_name: "Barge" +} +item { + name: "/m/02vqfm" + id: 452 + display_name: "Coffee" +} +item { + name: "/m/06__v" + id: 453 + display_name: "Snowboard" +} +item { + name: "/m/043nyj" + id: 454 + display_name: "Common fig" +} +item { + name: "/m/0grw1" + id: 455 + display_name: "Salad" +} +item { + name: "/m/03hl4l9" + id: 456 + display_name: "Marine invertebrates" +} +item { + name: "/m/0hnnb" + id: 457 + display_name: "Umbrella" +} +item { + name: "/m/04c0y" + id: 458 + display_name: "Kangaroo" +} +item { + name: "/m/0dzf4" + id: 459 + display_name: "Human arm" +} +item { + name: "/m/07v9_z" + id: 460 + display_name: "Measuring cup" +} +item { + name: "/m/0f9_l" + id: 461 + display_name: "Snail" +} +item { + name: "/m/0703r8" + id: 462 + display_name: "Loveseat" +} +item { + name: "/m/01xyhv" + id: 463 + display_name: "Suit" +} +item { + name: "/m/01fh4r" + id: 464 + display_name: "Teapot" +} +item { + name: "/m/04dr76w" + id: 465 + display_name: "Bottle" +} +item { + name: "/m/0pcr" + id: 466 + display_name: "Alpaca" +} +item { + name: "/m/03s_tn" + id: 467 + display_name: "Kettle" +} +item { + name: "/m/07mhn" + id: 468 + display_name: "Trousers" +} +item { + name: "/m/01hrv5" + id: 469 + display_name: "Popcorn" +} +item { + name: "/m/019h78" + id: 470 + display_name: "Centipede" +} +item { + name: "/m/09kmb" + id: 471 + display_name: "Spider" +} +item { + name: "/m/0h23m" + id: 472 + display_name: "Sparrow" +} +item { + name: "/m/050gv4" + id: 473 + display_name: "Plate" +} +item { + name: "/m/01fb_0" + id: 474 + display_name: "Bagel" +} +item { + name: "/m/02w3_ws" + id: 475 + display_name: "Personal care" +} +item { + name: "/m/014j1m" + id: 476 + display_name: "Apple" +} +item { + name: "/m/01gmv2" + id: 477 + display_name: "Brassiere" +} +item { + name: "/m/04y4h8h" + id: 478 + display_name: "Bathroom cabinet" +} +item { + name: "/m/026qbn5" + id: 479 + display_name: "Studio couch" +} +item { + name: "/m/01m2v" + id: 480 + display_name: "Computer keyboard" +} +item { + name: "/m/05_5p_0" + id: 481 + display_name: "Table tennis racket" +} +item { + name: "/m/07030" + id: 482 + display_name: "Sushi" +} +item { + name: "/m/01s105" + id: 483 + display_name: "Cabinetry" +} +item { + name: "/m/033rq4" + id: 484 + display_name: "Street light" +} +item { + name: "/m/0162_1" + id: 485 + display_name: "Towel" +} +item { + name: "/m/02z51p" + id: 486 + display_name: "Nightstand" +} +item { + name: "/m/06mf6" + id: 487 + display_name: "Rabbit" +} +item { + name: "/m/02hj4" + id: 488 + display_name: "Dolphin" +} +item { + name: "/m/0bt9lr" + id: 489 + display_name: "Dog" +} +item { + name: "/m/08hvt4" + id: 490 + display_name: "Jug" +} +item { + name: "/m/084rd" + id: 491 + display_name: "Wok" +} +item { + name: "/m/01pns0" + id: 492 + display_name: "Fire hydrant" +} +item { + name: "/m/014sv8" + id: 493 + display_name: "Human eye" +} +item { + name: "/m/079cl" + id: 494 + display_name: "Skyscraper" +} +item { + name: "/m/01940j" + id: 495 + display_name: "Backpack" +} +item { + name: "/m/05vtc" + id: 496 + display_name: "Potato" +} +item { + name: "/m/02w3r3" + id: 497 + display_name: "Paper towel" +} +item { + name: "/m/054xkw" + id: 498 + display_name: "Lifejacket" +} +item { + name: "/m/01bqk0" + id: 499 + display_name: "Bicycle wheel" +} +item { + name: "/m/09g1w" + id: 500 + display_name: "Toilet" +} diff --git a/workspace/virtuallab/object_detection/data/oid_v4_label_map.pbtxt b/workspace/virtuallab/object_detection/data/oid_v4_label_map.pbtxt new file mode 100644 index 0000000..643b9e8 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/oid_v4_label_map.pbtxt @@ -0,0 +1,3005 @@ +item { + name: "/m/011k07" + id: 1 + display_name: "Tortoise" +} +item { + name: "/m/011q46kg" + id: 2 + display_name: "Container" +} +item { + name: "/m/012074" + id: 3 + display_name: "Magpie" +} +item { + name: "/m/0120dh" + id: 4 + display_name: "Sea turtle" +} +item { + name: "/m/01226z" + id: 5 + display_name: "Football" +} +item { + name: "/m/012n7d" + id: 6 + display_name: "Ambulance" +} +item { + name: "/m/012w5l" + id: 7 + display_name: "Ladder" +} +item { + name: "/m/012xff" + id: 8 + display_name: "Toothbrush" +} +item { + name: "/m/012ysf" + id: 9 + display_name: "Syringe" +} +item { + name: "/m/0130jx" + id: 10 + display_name: "Sink" +} +item { + name: "/m/0138tl" + id: 11 + display_name: "Toy" +} +item { + name: "/m/013y1f" + id: 12 + display_name: "Organ" +} +item { + name: "/m/01432t" + id: 13 + display_name: "Cassette deck" +} +item { + name: "/m/014j1m" + id: 14 + display_name: "Apple" +} +item { + name: "/m/014sv8" + id: 15 + display_name: "Human eye" +} +item { + name: "/m/014trl" + id: 16 + display_name: "Cosmetics" +} +item { + name: "/m/014y4n" + id: 17 + display_name: "Paddle" +} +item { + name: "/m/0152hh" + id: 18 + display_name: "Snowman" +} +item { + name: "/m/01599" + id: 19 + display_name: "Beer" +} +item { + name: "/m/01_5g" + id: 20 + display_name: "Chopsticks" +} +item { + name: "/m/015h_t" + id: 21 + display_name: "Human beard" +} +item { + name: "/m/015p6" + id: 22 + display_name: "Bird" +} +item { + name: "/m/015qbp" + id: 23 + display_name: "Parking meter" +} +item { + name: "/m/015qff" + id: 24 + display_name: "Traffic light" +} +item { + name: "/m/015wgc" + id: 25 + display_name: "Croissant" +} +item { + name: "/m/015x4r" + id: 26 + display_name: "Cucumber" +} +item { + name: "/m/015x5n" + id: 27 + display_name: "Radish" +} +item { + name: "/m/0162_1" + id: 28 + display_name: "Towel" +} +item { + name: "/m/0167gd" + id: 29 + display_name: "Doll" +} +item { + name: "/m/016m2d" + id: 30 + display_name: "Skull" +} +item { + name: "/m/0174k2" + id: 31 + display_name: "Washing machine" +} +item { + name: "/m/0174n1" + id: 32 + display_name: "Glove" +} +item { + name: "/m/0175cv" + id: 33 + display_name: "Tick" +} +item { + name: "/m/0176mf" + id: 34 + display_name: "Belt" +} +item { + name: "/m/017ftj" + id: 35 + display_name: "Sunglasses" +} +item { + name: "/m/018j2" + id: 36 + display_name: "Banjo" +} +item { + name: "/m/018p4k" + id: 37 + display_name: "Cart" +} +item { + name: "/m/018xm" + id: 38 + display_name: "Ball" +} +item { + name: "/m/01940j" + id: 39 + display_name: "Backpack" +} +item { + name: "/m/0199g" + id: 40 + display_name: "Bicycle" +} +item { + name: "/m/019dx1" + id: 41 + display_name: "Home appliance" +} +item { + name: "/m/019h78" + id: 42 + display_name: "Centipede" +} +item { + name: "/m/019jd" + id: 43 + display_name: "Boat" +} +item { + name: "/m/019w40" + id: 44 + display_name: "Surfboard" +} +item { + name: "/m/01b638" + id: 45 + display_name: "Boot" +} +item { + name: "/m/01b7fy" + id: 46 + display_name: "Headphones" +} +item { + name: "/m/01b9xk" + id: 47 + display_name: "Hot dog" +} +item { + name: "/m/01bfm9" + id: 48 + display_name: "Shorts" +} +item { + name: "/m/01_bhs" + id: 49 + display_name: "Fast food" +} +item { + name: "/m/01bjv" + id: 50 + display_name: "Bus" +} +item { + name: "/m/01bl7v" + id: 51 + display_name: "Boy" +} +item { + name: "/m/01bms0" + id: 52 + display_name: "Screwdriver" +} +item { + name: "/m/01bqk0" + id: 53 + display_name: "Bicycle wheel" +} +item { + name: "/m/01btn" + id: 54 + display_name: "Barge" +} +item { + name: "/m/01c648" + id: 55 + display_name: "Laptop" +} +item { + name: "/m/01cmb2" + id: 56 + display_name: "Miniskirt" +} +item { + name: "/m/01d380" + id: 57 + display_name: "Drill" +} +item { + name: "/m/01d40f" + id: 58 + display_name: "Dress" +} +item { + name: "/m/01dws" + id: 59 + display_name: "Bear" +} +item { + name: "/m/01dwsz" + id: 60 + display_name: "Waffle" +} +item { + name: "/m/01dwwc" + id: 61 + display_name: "Pancake" +} +item { + name: "/m/01dxs" + id: 62 + display_name: "Brown bear" +} +item { + name: "/m/01dy8n" + id: 63 + display_name: "Woodpecker" +} +item { + name: "/m/01f8m5" + id: 64 + display_name: "Blue jay" +} +item { + name: "/m/01f91_" + id: 65 + display_name: "Pretzel" +} +item { + name: "/m/01fb_0" + id: 66 + display_name: "Bagel" +} +item { + name: "/m/01fdzj" + id: 67 + display_name: "Tower" +} +item { + name: "/m/01fh4r" + id: 68 + display_name: "Teapot" +} +item { + name: "/m/01g317" + id: 69 + display_name: "Person" +} +item { + name: "/m/01g3x7" + id: 70 + display_name: "Bow and arrow" +} +item { + name: "/m/01gkx_" + id: 71 + display_name: "Swimwear" +} +item { + name: "/m/01gllr" + id: 72 + display_name: "Beehive" +} +item { + name: "/m/01gmv2" + id: 73 + display_name: "Brassiere" +} +item { + name: "/m/01h3n" + id: 74 + display_name: "Bee" +} +item { + name: "/m/01h44" + id: 75 + display_name: "Bat" +} +item { + name: "/m/01h8tj" + id: 76 + display_name: "Starfish" +} +item { + name: "/m/01hrv5" + id: 77 + display_name: "Popcorn" +} +item { + name: "/m/01j3zr" + id: 78 + display_name: "Burrito" +} +item { + name: "/m/01j4z9" + id: 79 + display_name: "Chainsaw" +} +item { + name: "/m/01j51" + id: 80 + display_name: "Balloon" +} +item { + name: "/m/01j5ks" + id: 81 + display_name: "Wrench" +} +item { + name: "/m/01j61q" + id: 82 + display_name: "Tent" +} +item { + name: "/m/01jfm_" + id: 83 + display_name: "Vehicle registration plate" +} +item { + name: "/m/01jfsr" + id: 84 + display_name: "Lantern" +} +item { + name: "/m/01k6s3" + id: 85 + display_name: "Toaster" +} +item { + name: "/m/01kb5b" + id: 86 + display_name: "Flashlight" +} +item { + name: "/m/01knjb" + id: 87 + display_name: "Billboard" +} +item { + name: "/m/01krhy" + id: 88 + display_name: "Tiara" +} +item { + name: "/m/01lcw4" + id: 89 + display_name: "Limousine" +} +item { + name: "/m/01llwg" + id: 90 + display_name: "Necklace" +} +item { + name: "/m/01lrl" + id: 91 + display_name: "Carnivore" +} +item { + name: "/m/01lsmm" + id: 92 + display_name: "Scissors" +} +item { + name: "/m/01lynh" + id: 93 + display_name: "Stairs" +} +item { + name: "/m/01m2v" + id: 94 + display_name: "Computer keyboard" +} +item { + name: "/m/01m4t" + id: 95 + display_name: "Printer" +} +item { + name: "/m/01mqdt" + id: 96 + display_name: "Traffic sign" +} +item { + name: "/m/01mzpv" + id: 97 + display_name: "Chair" +} +item { + name: "/m/01n4qj" + id: 98 + display_name: "Shirt" +} +item { + name: "/m/01n5jq" + id: 99 + display_name: "Poster" +} +item { + name: "/m/01nkt" + id: 100 + display_name: "Cheese" +} +item { + name: "/m/01nq26" + id: 101 + display_name: "Sock" +} +item { + name: "/m/01pns0" + id: 102 + display_name: "Fire hydrant" +} +item { + name: "/m/01prls" + id: 103 + display_name: "Land vehicle" +} +item { + name: "/m/01r546" + id: 104 + display_name: "Earrings" +} +item { + name: "/m/01rkbr" + id: 105 + display_name: "Tie" +} +item { + name: "/m/01rzcn" + id: 106 + display_name: "Watercraft" +} +item { + name: "/m/01s105" + id: 107 + display_name: "Cabinetry" +} +item { + name: "/m/01s55n" + id: 108 + display_name: "Suitcase" +} +item { + name: "/m/01tcjp" + id: 109 + display_name: "Muffin" +} +item { + name: "/m/01vbnl" + id: 110 + display_name: "Bidet" +} +item { + name: "/m/01ww8y" + id: 111 + display_name: "Snack" +} +item { + name: "/m/01x3jk" + id: 112 + display_name: "Snowmobile" +} +item { + name: "/m/01x3z" + id: 113 + display_name: "Clock" +} +item { + name: "/m/01xgg_" + id: 114 + display_name: "Medical equipment" +} +item { + name: "/m/01xq0k1" + id: 115 + display_name: "Cattle" +} +item { + name: "/m/01xqw" + id: 116 + display_name: "Cello" +} +item { + name: "/m/01xs3r" + id: 117 + display_name: "Jet ski" +} +item { + name: "/m/01x_v" + id: 118 + display_name: "Camel" +} +item { + name: "/m/01xygc" + id: 119 + display_name: "Coat" +} +item { + name: "/m/01xyhv" + id: 120 + display_name: "Suit" +} +item { + name: "/m/01y9k5" + id: 121 + display_name: "Desk" +} +item { + name: "/m/01yrx" + id: 122 + display_name: "Cat" +} +item { + name: "/m/01yx86" + id: 123 + display_name: "Bronze sculpture" +} +item { + name: "/m/01z1kdw" + id: 124 + display_name: "Juice" +} +item { + name: "/m/02068x" + id: 125 + display_name: "Gondola" +} +item { + name: "/m/020jm" + id: 126 + display_name: "Beetle" +} +item { + name: "/m/020kz" + id: 127 + display_name: "Cannon" +} +item { + name: "/m/020lf" + id: 128 + display_name: "Computer mouse" +} +item { + name: "/m/021mn" + id: 129 + display_name: "Cookie" +} +item { + name: "/m/021sj1" + id: 130 + display_name: "Office building" +} +item { + name: "/m/0220r2" + id: 131 + display_name: "Fountain" +} +item { + name: "/m/0242l" + id: 132 + display_name: "Coin" +} +item { + name: "/m/024d2" + id: 133 + display_name: "Calculator" +} +item { + name: "/m/024g6" + id: 134 + display_name: "Cocktail" +} +item { + name: "/m/02522" + id: 135 + display_name: "Computer monitor" +} +item { + name: "/m/025dyy" + id: 136 + display_name: "Box" +} +item { + name: "/m/025fsf" + id: 137 + display_name: "Stapler" +} +item { + name: "/m/025nd" + id: 138 + display_name: "Christmas tree" +} +item { + name: "/m/025rp__" + id: 139 + display_name: "Cowboy hat" +} +item { + name: "/m/0268lbt" + id: 140 + display_name: "Hiking equipment" +} +item { + name: "/m/026qbn5" + id: 141 + display_name: "Studio couch" +} +item { + name: "/m/026t6" + id: 142 + display_name: "Drum" +} +item { + name: "/m/0270h" + id: 143 + display_name: "Dessert" +} +item { + name: "/m/0271qf7" + id: 144 + display_name: "Wine rack" +} +item { + name: "/m/0271t" + id: 145 + display_name: "Drink" +} +item { + name: "/m/027pcv" + id: 146 + display_name: "Zucchini" +} +item { + name: "/m/027rl48" + id: 147 + display_name: "Ladle" +} +item { + name: "/m/0283dt1" + id: 148 + display_name: "Human mouth" +} +item { + name: "/m/0284d" + id: 149 + display_name: "Dairy" +} +item { + name: "/m/029b3" + id: 150 + display_name: "Dice" +} +item { + name: "/m/029bxz" + id: 151 + display_name: "Oven" +} +item { + name: "/m/029tx" + id: 152 + display_name: "Dinosaur" +} +item { + name: "/m/02bm9n" + id: 153 + display_name: "Ratchet" +} +item { + name: "/m/02crq1" + id: 154 + display_name: "Couch" +} +item { + name: "/m/02ctlc" + id: 155 + display_name: "Cricket ball" +} +item { + name: "/m/02cvgx" + id: 156 + display_name: "Winter melon" +} +item { + name: "/m/02d1br" + id: 157 + display_name: "Spatula" +} +item { + name: "/m/02d9qx" + id: 158 + display_name: "Whiteboard" +} +item { + name: "/m/02ddwp" + id: 159 + display_name: "Pencil sharpener" +} +item { + name: "/m/02dgv" + id: 160 + display_name: "Door" +} +item { + name: "/m/02dl1y" + id: 161 + display_name: "Hat" +} +item { + name: "/m/02f9f_" + id: 162 + display_name: "Shower" +} +item { + name: "/m/02fh7f" + id: 163 + display_name: "Eraser" +} +item { + name: "/m/02fq_6" + id: 164 + display_name: "Fedora" +} +item { + name: "/m/02g30s" + id: 165 + display_name: "Guacamole" +} +item { + name: "/m/02gzp" + id: 166 + display_name: "Dagger" +} +item { + name: "/m/02h19r" + id: 167 + display_name: "Scarf" +} +item { + name: "/m/02hj4" + id: 168 + display_name: "Dolphin" +} +item { + name: "/m/02jfl0" + id: 169 + display_name: "Sombrero" +} +item { + name: "/m/02jnhm" + id: 170 + display_name: "Tin can" +} +item { + name: "/m/02jvh9" + id: 171 + display_name: "Mug" +} +item { + name: "/m/02jz0l" + id: 172 + display_name: "Tap" +} +item { + name: "/m/02l8p9" + id: 173 + display_name: "Harbor seal" +} +item { + name: "/m/02lbcq" + id: 174 + display_name: "Stretcher" +} +item { + name: "/m/02mqfb" + id: 175 + display_name: "Can opener" +} +item { + name: "/m/02_n6y" + id: 176 + display_name: "Goggles" +} +item { + name: "/m/02p0tk3" + id: 177 + display_name: "Human body" +} +item { + name: "/m/02p3w7d" + id: 178 + display_name: "Roller skates" +} +item { + name: "/m/02p5f1q" + id: 179 + display_name: "Coffee cup" +} +item { + name: "/m/02pdsw" + id: 180 + display_name: "Cutting board" +} +item { + name: "/m/02pjr4" + id: 181 + display_name: "Blender" +} +item { + name: "/m/02pkr5" + id: 182 + display_name: "Plumbing fixture" +} +item { + name: "/m/02pv19" + id: 183 + display_name: "Stop sign" +} +item { + name: "/m/02rdsp" + id: 184 + display_name: "Office supplies" +} +item { + name: "/m/02rgn06" + id: 185 + display_name: "Volleyball" +} +item { + name: "/m/02s195" + id: 186 + display_name: "Vase" +} +item { + name: "/m/02tsc9" + id: 187 + display_name: "Slow cooker" +} +item { + name: "/m/02vkqh8" + id: 188 + display_name: "Wardrobe" +} +item { + name: "/m/02vqfm" + id: 189 + display_name: "Coffee" +} +item { + name: "/m/02vwcm" + id: 190 + display_name: "Whisk" +} +item { + name: "/m/02w3r3" + id: 191 + display_name: "Paper towel" +} +item { + name: "/m/02w3_ws" + id: 192 + display_name: "Personal care" +} +item { + name: "/m/02wbm" + id: 193 + display_name: "Food" +} +item { + name: "/m/02wbtzl" + id: 194 + display_name: "Sun hat" +} +item { + name: "/m/02wg_p" + id: 195 + display_name: "Tree house" +} +item { + name: "/m/02wmf" + id: 196 + display_name: "Flying disc" +} +item { + name: "/m/02wv6h6" + id: 197 + display_name: "Skirt" +} +item { + name: "/m/02wv84t" + id: 198 + display_name: "Gas stove" +} +item { + name: "/m/02x8cch" + id: 199 + display_name: "Salt and pepper shakers" +} +item { + name: "/m/02x984l" + id: 200 + display_name: "Mechanical fan" +} +item { + name: "/m/02xb7qb" + id: 201 + display_name: "Face powder" +} +item { + name: "/m/02xqq" + id: 202 + display_name: "Fax" +} +item { + name: "/m/02xwb" + id: 203 + display_name: "Fruit" +} +item { + name: "/m/02y6n" + id: 204 + display_name: "French fries" +} +item { + name: "/m/02z51p" + id: 205 + display_name: "Nightstand" +} +item { + name: "/m/02zn6n" + id: 206 + display_name: "Barrel" +} +item { + name: "/m/02zt3" + id: 207 + display_name: "Kite" +} +item { + name: "/m/02zvsm" + id: 208 + display_name: "Tart" +} +item { + name: "/m/030610" + id: 209 + display_name: "Treadmill" +} +item { + name: "/m/0306r" + id: 210 + display_name: "Fox" +} +item { + name: "/m/03120" + id: 211 + display_name: "Flag" +} +item { + name: "/m/0319l" + id: 212 + display_name: "Horn" +} +item { + name: "/m/031b6r" + id: 213 + display_name: "Window blind" +} +item { + name: "/m/031n1" + id: 214 + display_name: "Human foot" +} +item { + name: "/m/0323sq" + id: 215 + display_name: "Golf cart" +} +item { + name: "/m/032b3c" + id: 216 + display_name: "Jacket" +} +item { + name: "/m/033cnk" + id: 217 + display_name: "Egg" +} +item { + name: "/m/033rq4" + id: 218 + display_name: "Street light" +} +item { + name: "/m/0342h" + id: 219 + display_name: "Guitar" +} +item { + name: "/m/034c16" + id: 220 + display_name: "Pillow" +} +item { + name: "/m/035r7c" + id: 221 + display_name: "Human leg" +} +item { + name: "/m/035vxb" + id: 222 + display_name: "Isopod" +} +item { + name: "/m/0388q" + id: 223 + display_name: "Grape" +} +item { + name: "/m/039xj_" + id: 224 + display_name: "Human ear" +} +item { + name: "/m/03bbps" + id: 225 + display_name: "Power plugs and sockets" +} +item { + name: "/m/03bj1" + id: 226 + display_name: "Panda" +} +item { + name: "/m/03bk1" + id: 227 + display_name: "Giraffe" +} +item { + name: "/m/03bt1vf" + id: 228 + display_name: "Woman" +} +item { + name: "/m/03c7gz" + id: 229 + display_name: "Door handle" +} +item { + name: "/m/03d443" + id: 230 + display_name: "Rhinoceros" +} +item { + name: "/m/03dnzn" + id: 231 + display_name: "Bathtub" +} +item { + name: "/m/03fj2" + id: 232 + display_name: "Goldfish" +} +item { + name: "/m/03fp41" + id: 233 + display_name: "Houseplant" +} +item { + name: "/m/03fwl" + id: 234 + display_name: "Goat" +} +item { + name: "/m/03g8mr" + id: 235 + display_name: "Baseball bat" +} +item { + name: "/m/03grzl" + id: 236 + display_name: "Baseball glove" +} +item { + name: "/m/03hj559" + id: 237 + display_name: "Mixing bowl" +} +item { + name: "/m/03hl4l9" + id: 238 + display_name: "Marine invertebrates" +} +item { + name: "/m/03hlz0c" + id: 239 + display_name: "Kitchen utensil" +} +item { + name: "/m/03jbxj" + id: 240 + display_name: "Light switch" +} +item { + name: "/m/03jm5" + id: 241 + display_name: "House" +} +item { + name: "/m/03k3r" + id: 242 + display_name: "Horse" +} +item { + name: "/m/03kt2w" + id: 243 + display_name: "Stationary bicycle" +} +item { + name: "/m/03l9g" + id: 244 + display_name: "Hammer" +} +item { + name: "/m/03ldnb" + id: 245 + display_name: "Ceiling fan" +} +item { + name: "/m/03m3pdh" + id: 246 + display_name: "Sofa bed" +} +item { + name: "/m/03m3vtv" + id: 247 + display_name: "Adhesive tape" +} +item { + name: "/m/03m5k" + id: 248 + display_name: "Harp" +} +item { + name: "/m/03nfch" + id: 249 + display_name: "Sandal" +} +item { + name: "/m/03p3bw" + id: 250 + display_name: "Bicycle helmet" +} +item { + name: "/m/03q5c7" + id: 251 + display_name: "Saucer" +} +item { + name: "/m/03q5t" + id: 252 + display_name: "Harpsichord" +} +item { + name: "/m/03q69" + id: 253 + display_name: "Human hair" +} +item { + name: "/m/03qhv5" + id: 254 + display_name: "Heater" +} +item { + name: "/m/03qjg" + id: 255 + display_name: "Harmonica" +} +item { + name: "/m/03qrc" + id: 256 + display_name: "Hamster" +} +item { + name: "/m/03rszm" + id: 257 + display_name: "Curtain" +} +item { + name: "/m/03ssj5" + id: 258 + display_name: "Bed" +} +item { + name: "/m/03s_tn" + id: 259 + display_name: "Kettle" +} +item { + name: "/m/03tw93" + id: 260 + display_name: "Fireplace" +} +item { + name: "/m/03txqz" + id: 261 + display_name: "Scale" +} +item { + name: "/m/03v5tg" + id: 262 + display_name: "Drinking straw" +} +item { + name: "/m/03vt0" + id: 263 + display_name: "Insect" +} +item { + name: "/m/03wvsk" + id: 264 + display_name: "Hair dryer" +} +item { + name: "/m/03_wxk" + id: 265 + display_name: "Kitchenware" +} +item { + name: "/m/03wym" + id: 266 + display_name: "Indoor rower" +} +item { + name: "/m/03xxp" + id: 267 + display_name: "Invertebrate" +} +item { + name: "/m/03y6mg" + id: 268 + display_name: "Food processor" +} +item { + name: "/m/03__z0" + id: 269 + display_name: "Bookcase" +} +item { + name: "/m/040b_t" + id: 270 + display_name: "Refrigerator" +} +item { + name: "/m/04169hn" + id: 271 + display_name: "Wood-burning stove" +} +item { + name: "/m/0420v5" + id: 272 + display_name: "Punching bag" +} +item { + name: "/m/043nyj" + id: 273 + display_name: "Common fig" +} +item { + name: "/m/0440zs" + id: 274 + display_name: "Cocktail shaker" +} +item { + name: "/m/0449p" + id: 275 + display_name: "Jaguar" +} +item { + name: "/m/044r5d" + id: 276 + display_name: "Golf ball" +} +item { + name: "/m/0463sg" + id: 277 + display_name: "Fashion accessory" +} +item { + name: "/m/046dlr" + id: 278 + display_name: "Alarm clock" +} +item { + name: "/m/047j0r" + id: 279 + display_name: "Filing cabinet" +} +item { + name: "/m/047v4b" + id: 280 + display_name: "Artichoke" +} +item { + name: "/m/04bcr3" + id: 281 + display_name: "Table" +} +item { + name: "/m/04brg2" + id: 282 + display_name: "Tableware" +} +item { + name: "/m/04c0y" + id: 283 + display_name: "Kangaroo" +} +item { + name: "/m/04cp_" + id: 284 + display_name: "Koala" +} +item { + name: "/m/04ctx" + id: 285 + display_name: "Knife" +} +item { + name: "/m/04dr76w" + id: 286 + display_name: "Bottle" +} +item { + name: "/m/04f5ws" + id: 287 + display_name: "Bottle opener" +} +item { + name: "/m/04g2r" + id: 288 + display_name: "Lynx" +} +item { + name: "/m/04gth" + id: 289 + display_name: "Lavender" +} +item { + name: "/m/04h7h" + id: 290 + display_name: "Lighthouse" +} +item { + name: "/m/04h8sr" + id: 291 + display_name: "Dumbbell" +} +item { + name: "/m/04hgtk" + id: 292 + display_name: "Human head" +} +item { + name: "/m/04kkgm" + id: 293 + display_name: "Bowl" +} +item { + name: "/m/04lvq_" + id: 294 + display_name: "Humidifier" +} +item { + name: "/m/04m6gz" + id: 295 + display_name: "Porch" +} +item { + name: "/m/04m9y" + id: 296 + display_name: "Lizard" +} +item { + name: "/m/04p0qw" + id: 297 + display_name: "Billiard table" +} +item { + name: "/m/04rky" + id: 298 + display_name: "Mammal" +} +item { + name: "/m/04rmv" + id: 299 + display_name: "Mouse" +} +item { + name: "/m/04_sv" + id: 300 + display_name: "Motorcycle" +} +item { + name: "/m/04szw" + id: 301 + display_name: "Musical instrument" +} +item { + name: "/m/04tn4x" + id: 302 + display_name: "Swim cap" +} +item { + name: "/m/04v6l4" + id: 303 + display_name: "Frying pan" +} +item { + name: "/m/04vv5k" + id: 304 + display_name: "Snowplow" +} +item { + name: "/m/04y4h8h" + id: 305 + display_name: "Bathroom cabinet" +} +item { + name: "/m/04ylt" + id: 306 + display_name: "Missile" +} +item { + name: "/m/04yqq2" + id: 307 + display_name: "Bust" +} +item { + name: "/m/04yx4" + id: 308 + display_name: "Man" +} +item { + name: "/m/04z4wx" + id: 309 + display_name: "Waffle iron" +} +item { + name: "/m/04zpv" + id: 310 + display_name: "Milk" +} +item { + name: "/m/04zwwv" + id: 311 + display_name: "Ring binder" +} +item { + name: "/m/050gv4" + id: 312 + display_name: "Plate" +} +item { + name: "/m/050k8" + id: 313 + display_name: "Mobile phone" +} +item { + name: "/m/052lwg6" + id: 314 + display_name: "Baked goods" +} +item { + name: "/m/052sf" + id: 315 + display_name: "Mushroom" +} +item { + name: "/m/05441v" + id: 316 + display_name: "Crutch" +} +item { + name: "/m/054fyh" + id: 317 + display_name: "Pitcher" +} +item { + name: "/m/054_l" + id: 318 + display_name: "Mirror" +} +item { + name: "/m/054xkw" + id: 319 + display_name: "Lifejacket" +} +item { + name: "/m/05_5p_0" + id: 320 + display_name: "Table tennis racket" +} +item { + name: "/m/05676x" + id: 321 + display_name: "Pencil case" +} +item { + name: "/m/057cc" + id: 322 + display_name: "Musical keyboard" +} +item { + name: "/m/057p5t" + id: 323 + display_name: "Scoreboard" +} +item { + name: "/m/0584n8" + id: 324 + display_name: "Briefcase" +} +item { + name: "/m/058qzx" + id: 325 + display_name: "Kitchen knife" +} +item { + name: "/m/05bm6" + id: 326 + display_name: "Nail" +} +item { + name: "/m/05ctyq" + id: 327 + display_name: "Tennis ball" +} +item { + name: "/m/05gqfk" + id: 328 + display_name: "Plastic bag" +} +item { + name: "/m/05kms" + id: 329 + display_name: "Oboe" +} +item { + name: "/m/05kyg_" + id: 330 + display_name: "Chest of drawers" +} +item { + name: "/m/05n4y" + id: 331 + display_name: "Ostrich" +} +item { + name: "/m/05r5c" + id: 332 + display_name: "Piano" +} +item { + name: "/m/05r655" + id: 333 + display_name: "Girl" +} +item { + name: "/m/05s2s" + id: 334 + display_name: "Plant" +} +item { + name: "/m/05vtc" + id: 335 + display_name: "Potato" +} +item { + name: "/m/05w9t9" + id: 336 + display_name: "Hair spray" +} +item { + name: "/m/05y5lj" + id: 337 + display_name: "Sports equipment" +} +item { + name: "/m/05z55" + id: 338 + display_name: "Pasta" +} +item { + name: "/m/05z6w" + id: 339 + display_name: "Penguin" +} +item { + name: "/m/05zsy" + id: 340 + display_name: "Pumpkin" +} +item { + name: "/m/061_f" + id: 341 + display_name: "Pear" +} +item { + name: "/m/061hd_" + id: 342 + display_name: "Infant bed" +} +item { + name: "/m/0633h" + id: 343 + display_name: "Polar bear" +} +item { + name: "/m/063rgb" + id: 344 + display_name: "Mixer" +} +item { + name: "/m/0642b4" + id: 345 + display_name: "Cupboard" +} +item { + name: "/m/065h6l" + id: 346 + display_name: "Jacuzzi" +} +item { + name: "/m/0663v" + id: 347 + display_name: "Pizza" +} +item { + name: "/m/06_72j" + id: 348 + display_name: "Digital clock" +} +item { + name: "/m/068zj" + id: 349 + display_name: "Pig" +} +item { + name: "/m/06bt6" + id: 350 + display_name: "Reptile" +} +item { + name: "/m/06c54" + id: 351 + display_name: "Rifle" +} +item { + name: "/m/06c7f7" + id: 352 + display_name: "Lipstick" +} +item { + name: "/m/06_fw" + id: 353 + display_name: "Skateboard" +} +item { + name: "/m/06j2d" + id: 354 + display_name: "Raven" +} +item { + name: "/m/06k2mb" + id: 355 + display_name: "High heels" +} +item { + name: "/m/06l9r" + id: 356 + display_name: "Red panda" +} +item { + name: "/m/06m11" + id: 357 + display_name: "Rose" +} +item { + name: "/m/06mf6" + id: 358 + display_name: "Rabbit" +} +item { + name: "/m/06msq" + id: 359 + display_name: "Sculpture" +} +item { + name: "/m/06ncr" + id: 360 + display_name: "Saxophone" +} +item { + name: "/m/06nrc" + id: 361 + display_name: "Shotgun" +} +item { + name: "/m/06nwz" + id: 362 + display_name: "Seafood" +} +item { + name: "/m/06pcq" + id: 363 + display_name: "Submarine sandwich" +} +item { + name: "/m/06__v" + id: 364 + display_name: "Snowboard" +} +item { + name: "/m/06y5r" + id: 365 + display_name: "Sword" +} +item { + name: "/m/06z37_" + id: 366 + display_name: "Picture frame" +} +item { + name: "/m/07030" + id: 367 + display_name: "Sushi" +} +item { + name: "/m/0703r8" + id: 368 + display_name: "Loveseat" +} +item { + name: "/m/071p9" + id: 369 + display_name: "Ski" +} +item { + name: "/m/071qp" + id: 370 + display_name: "Squirrel" +} +item { + name: "/m/073bxn" + id: 371 + display_name: "Tripod" +} +item { + name: "/m/073g6" + id: 372 + display_name: "Stethoscope" +} +item { + name: "/m/074d1" + id: 373 + display_name: "Submarine" +} +item { + name: "/m/0755b" + id: 374 + display_name: "Scorpion" +} +item { + name: "/m/076bq" + id: 375 + display_name: "Segway" +} +item { + name: "/m/076lb9" + id: 376 + display_name: "Training bench" +} +item { + name: "/m/078jl" + id: 377 + display_name: "Snake" +} +item { + name: "/m/078n6m" + id: 378 + display_name: "Coffee table" +} +item { + name: "/m/079cl" + id: 379 + display_name: "Skyscraper" +} +item { + name: "/m/07bgp" + id: 380 + display_name: "Sheep" +} +item { + name: "/m/07c52" + id: 381 + display_name: "Television" +} +item { + name: "/m/07c6l" + id: 382 + display_name: "Trombone" +} +item { + name: "/m/07clx" + id: 383 + display_name: "Tea" +} +item { + name: "/m/07cmd" + id: 384 + display_name: "Tank" +} +item { + name: "/m/07crc" + id: 385 + display_name: "Taco" +} +item { + name: "/m/07cx4" + id: 386 + display_name: "Telephone" +} +item { + name: "/m/07dd4" + id: 387 + display_name: "Torch" +} +item { + name: "/m/07dm6" + id: 388 + display_name: "Tiger" +} +item { + name: "/m/07fbm7" + id: 389 + display_name: "Strawberry" +} +item { + name: "/m/07gql" + id: 390 + display_name: "Trumpet" +} +item { + name: "/m/07j7r" + id: 391 + display_name: "Tree" +} +item { + name: "/m/07j87" + id: 392 + display_name: "Tomato" +} +item { + name: "/m/07jdr" + id: 393 + display_name: "Train" +} +item { + name: "/m/07k1x" + id: 394 + display_name: "Tool" +} +item { + name: "/m/07kng9" + id: 395 + display_name: "Picnic basket" +} +item { + name: "/m/07mcwg" + id: 396 + display_name: "Cooking spray" +} +item { + name: "/m/07mhn" + id: 397 + display_name: "Trousers" +} +item { + name: "/m/07pj7bq" + id: 398 + display_name: "Bowling equipment" +} +item { + name: "/m/07qxg_" + id: 399 + display_name: "Football helmet" +} +item { + name: "/m/07r04" + id: 400 + display_name: "Truck" +} +item { + name: "/m/07v9_z" + id: 401 + display_name: "Measuring cup" +} +item { + name: "/m/07xyvk" + id: 402 + display_name: "Coffeemaker" +} +item { + name: "/m/07y_7" + id: 403 + display_name: "Violin" +} +item { + name: "/m/07yv9" + id: 404 + display_name: "Vehicle" +} +item { + name: "/m/080hkjn" + id: 405 + display_name: "Handbag" +} +item { + name: "/m/080n7g" + id: 406 + display_name: "Paper cutter" +} +item { + name: "/m/081qc" + id: 407 + display_name: "Wine" +} +item { + name: "/m/083kb" + id: 408 + display_name: "Weapon" +} +item { + name: "/m/083wq" + id: 409 + display_name: "Wheel" +} +item { + name: "/m/084hf" + id: 410 + display_name: "Worm" +} +item { + name: "/m/084rd" + id: 411 + display_name: "Wok" +} +item { + name: "/m/084zz" + id: 412 + display_name: "Whale" +} +item { + name: "/m/0898b" + id: 413 + display_name: "Zebra" +} +item { + name: "/m/08dz3q" + id: 414 + display_name: "Auto part" +} +item { + name: "/m/08hvt4" + id: 415 + display_name: "Jug" +} +item { + name: "/m/08ks85" + id: 416 + display_name: "Pizza cutter" +} +item { + name: "/m/08p92x" + id: 417 + display_name: "Cream" +} +item { + name: "/m/08pbxl" + id: 418 + display_name: "Monkey" +} +item { + name: "/m/096mb" + id: 419 + display_name: "Lion" +} +item { + name: "/m/09728" + id: 420 + display_name: "Bread" +} +item { + name: "/m/099ssp" + id: 421 + display_name: "Platter" +} +item { + name: "/m/09b5t" + id: 422 + display_name: "Chicken" +} +item { + name: "/m/09csl" + id: 423 + display_name: "Eagle" +} +item { + name: "/m/09ct_" + id: 424 + display_name: "Helicopter" +} +item { + name: "/m/09d5_" + id: 425 + display_name: "Owl" +} +item { + name: "/m/09ddx" + id: 426 + display_name: "Duck" +} +item { + name: "/m/09dzg" + id: 427 + display_name: "Turtle" +} +item { + name: "/m/09f20" + id: 428 + display_name: "Hippopotamus" +} +item { + name: "/m/09f_2" + id: 429 + display_name: "Crocodile" +} +item { + name: "/m/09g1w" + id: 430 + display_name: "Toilet" +} +item { + name: "/m/09gtd" + id: 431 + display_name: "Toilet paper" +} +item { + name: "/m/09gys" + id: 432 + display_name: "Squid" +} +item { + name: "/m/09j2d" + id: 433 + display_name: "Clothing" +} +item { + name: "/m/09j5n" + id: 434 + display_name: "Footwear" +} +item { + name: "/m/09k_b" + id: 435 + display_name: "Lemon" +} +item { + name: "/m/09kmb" + id: 436 + display_name: "Spider" +} +item { + name: "/m/09kx5" + id: 437 + display_name: "Deer" +} +item { + name: "/m/09ld4" + id: 438 + display_name: "Frog" +} +item { + name: "/m/09qck" + id: 439 + display_name: "Banana" +} +item { + name: "/m/09rvcxw" + id: 440 + display_name: "Rocket" +} +item { + name: "/m/09tvcd" + id: 441 + display_name: "Wine glass" +} +item { + name: "/m/0b3fp9" + id: 442 + display_name: "Countertop" +} +item { + name: "/m/0bh9flk" + id: 443 + display_name: "Tablet computer" +} +item { + name: "/m/0bjyj5" + id: 444 + display_name: "Waste container" +} +item { + name: "/m/0b_rs" + id: 445 + display_name: "Swimming pool" +} +item { + name: "/m/0bt9lr" + id: 446 + display_name: "Dog" +} +item { + name: "/m/0bt_c3" + id: 447 + display_name: "Book" +} +item { + name: "/m/0bwd_0j" + id: 448 + display_name: "Elephant" +} +item { + name: "/m/0by6g" + id: 449 + display_name: "Shark" +} +item { + name: "/m/0c06p" + id: 450 + display_name: "Candle" +} +item { + name: "/m/0c29q" + id: 451 + display_name: "Leopard" +} +item { + name: "/m/0c2jj" + id: 452 + display_name: "Axe" +} +item { + name: "/m/0c3m8g" + id: 453 + display_name: "Hand dryer" +} +item { + name: "/m/0c3mkw" + id: 454 + display_name: "Soap dispenser" +} +item { + name: "/m/0c568" + id: 455 + display_name: "Porcupine" +} +item { + name: "/m/0c9ph5" + id: 456 + display_name: "Flower" +} +item { + name: "/m/0ccs93" + id: 457 + display_name: "Canary" +} +item { + name: "/m/0cd4d" + id: 458 + display_name: "Cheetah" +} +item { + name: "/m/0cdl1" + id: 459 + display_name: "Palm tree" +} +item { + name: "/m/0cdn1" + id: 460 + display_name: "Hamburger" +} +item { + name: "/m/0cffdh" + id: 461 + display_name: "Maple" +} +item { + name: "/m/0cgh4" + id: 462 + display_name: "Building" +} +item { + name: "/m/0ch_cf" + id: 463 + display_name: "Fish" +} +item { + name: "/m/0cjq5" + id: 464 + display_name: "Lobster" +} +item { + name: "/m/0cjs7" + id: 465 + display_name: "Asparagus" +} +item { + name: "/m/0c_jw" + id: 466 + display_name: "Furniture" +} +item { + name: "/m/0cl4p" + id: 467 + display_name: "Hedgehog" +} +item { + name: "/m/0cmf2" + id: 468 + display_name: "Airplane" +} +item { + name: "/m/0cmx8" + id: 469 + display_name: "Spoon" +} +item { + name: "/m/0cn6p" + id: 470 + display_name: "Otter" +} +item { + name: "/m/0cnyhnx" + id: 471 + display_name: "Bull" +} +item { + name: "/m/0_cp5" + id: 472 + display_name: "Oyster" +} +item { + name: "/m/0cqn2" + id: 473 + display_name: "Horizontal bar" +} +item { + name: "/m/0crjs" + id: 474 + display_name: "Convenience store" +} +item { + name: "/m/0ct4f" + id: 475 + display_name: "Bomb" +} +item { + name: "/m/0cvnqh" + id: 476 + display_name: "Bench" +} +item { + name: "/m/0cxn2" + id: 477 + display_name: "Ice cream" +} +item { + name: "/m/0cydv" + id: 478 + display_name: "Caterpillar" +} +item { + name: "/m/0cyf8" + id: 479 + display_name: "Butterfly" +} +item { + name: "/m/0cyfs" + id: 480 + display_name: "Parachute" +} +item { + name: "/m/0cyhj_" + id: 481 + display_name: "Orange" +} +item { + name: "/m/0czz2" + id: 482 + display_name: "Antelope" +} +item { + name: "/m/0d20w4" + id: 483 + display_name: "Beaker" +} +item { + name: "/m/0d_2m" + id: 484 + display_name: "Moths and butterflies" +} +item { + name: "/m/0d4v4" + id: 485 + display_name: "Window" +} +item { + name: "/m/0d4w1" + id: 486 + display_name: "Closet" +} +item { + name: "/m/0d5gx" + id: 487 + display_name: "Castle" +} +item { + name: "/m/0d8zb" + id: 488 + display_name: "Jellyfish" +} +item { + name: "/m/0dbvp" + id: 489 + display_name: "Goose" +} +item { + name: "/m/0dbzx" + id: 490 + display_name: "Mule" +} +item { + name: "/m/0dftk" + id: 491 + display_name: "Swan" +} +item { + name: "/m/0dj6p" + id: 492 + display_name: "Peach" +} +item { + name: "/m/0djtd" + id: 493 + display_name: "Coconut" +} +item { + name: "/m/0dkzw" + id: 494 + display_name: "Seat belt" +} +item { + name: "/m/0dq75" + id: 495 + display_name: "Raccoon" +} +item { + name: "/m/0_dqb" + id: 496 + display_name: "Chisel" +} +item { + name: "/m/0dt3t" + id: 497 + display_name: "Fork" +} +item { + name: "/m/0dtln" + id: 498 + display_name: "Lamp" +} +item { + name: "/m/0dv5r" + id: 499 + display_name: "Camera" +} +item { + name: "/m/0dv77" + id: 500 + display_name: "Squash" +} +item { + name: "/m/0dv9c" + id: 501 + display_name: "Racket" +} +item { + name: "/m/0dzct" + id: 502 + display_name: "Human face" +} +item { + name: "/m/0dzf4" + id: 503 + display_name: "Human arm" +} +item { + name: "/m/0f4s2w" + id: 504 + display_name: "Vegetable" +} +item { + name: "/m/0f571" + id: 505 + display_name: "Diaper" +} +item { + name: "/m/0f6nr" + id: 506 + display_name: "Unicycle" +} +item { + name: "/m/0f6wt" + id: 507 + display_name: "Falcon" +} +item { + name: "/m/0f8s22" + id: 508 + display_name: "Chime" +} +item { + name: "/m/0f9_l" + id: 509 + display_name: "Snail" +} +item { + name: "/m/0fbdv" + id: 510 + display_name: "Shellfish" +} +item { + name: "/m/0fbw6" + id: 511 + display_name: "Cabbage" +} +item { + name: "/m/0fj52s" + id: 512 + display_name: "Carrot" +} +item { + name: "/m/0fldg" + id: 513 + display_name: "Mango" +} +item { + name: "/m/0fly7" + id: 514 + display_name: "Jeans" +} +item { + name: "/m/0fm3zh" + id: 515 + display_name: "Flowerpot" +} +item { + name: "/m/0fp6w" + id: 516 + display_name: "Pineapple" +} +item { + name: "/m/0fqfqc" + id: 517 + display_name: "Drawer" +} +item { + name: "/m/0fqt361" + id: 518 + display_name: "Stool" +} +item { + name: "/m/0frqm" + id: 519 + display_name: "Envelope" +} +item { + name: "/m/0fszt" + id: 520 + display_name: "Cake" +} +item { + name: "/m/0ft9s" + id: 521 + display_name: "Dragonfly" +} +item { + name: "/m/0ftb8" + id: 522 + display_name: "Sunflower" +} +item { + name: "/m/0fx9l" + id: 523 + display_name: "Microwave oven" +} +item { + name: "/m/0fz0h" + id: 524 + display_name: "Honeycomb" +} +item { + name: "/m/0gd2v" + id: 525 + display_name: "Marine mammal" +} +item { + name: "/m/0gd36" + id: 526 + display_name: "Sea lion" +} +item { + name: "/m/0gj37" + id: 527 + display_name: "Ladybug" +} +item { + name: "/m/0gjbg72" + id: 528 + display_name: "Shelf" +} +item { + name: "/m/0gjkl" + id: 529 + display_name: "Watch" +} +item { + name: "/m/0gm28" + id: 530 + display_name: "Candy" +} +item { + name: "/m/0grw1" + id: 531 + display_name: "Salad" +} +item { + name: "/m/0gv1x" + id: 532 + display_name: "Parrot" +} +item { + name: "/m/0gxl3" + id: 533 + display_name: "Handgun" +} +item { + name: "/m/0h23m" + id: 534 + display_name: "Sparrow" +} +item { + name: "/m/0h2r6" + id: 535 + display_name: "Van" +} +item { + name: "/m/0h8jyh6" + id: 536 + display_name: "Grinder" +} +item { + name: "/m/0h8kx63" + id: 537 + display_name: "Spice rack" +} +item { + name: "/m/0h8l4fh" + id: 538 + display_name: "Light bulb" +} +item { + name: "/m/0h8lkj8" + id: 539 + display_name: "Corded phone" +} +item { + name: "/m/0h8mhzd" + id: 540 + display_name: "Sports uniform" +} +item { + name: "/m/0h8my_4" + id: 541 + display_name: "Tennis racket" +} +item { + name: "/m/0h8mzrc" + id: 542 + display_name: "Wall clock" +} +item { + name: "/m/0h8n27j" + id: 543 + display_name: "Serving tray" +} +item { + name: "/m/0h8n5zk" + id: 544 + display_name: "Kitchen & dining room table" +} +item { + name: "/m/0h8n6f9" + id: 545 + display_name: "Dog bed" +} +item { + name: "/m/0h8n6ft" + id: 546 + display_name: "Cake stand" +} +item { + name: "/m/0h8nm9j" + id: 547 + display_name: "Cat furniture" +} +item { + name: "/m/0h8nr_l" + id: 548 + display_name: "Bathroom accessory" +} +item { + name: "/m/0h8nsvg" + id: 549 + display_name: "Facial tissue holder" +} +item { + name: "/m/0h8ntjv" + id: 550 + display_name: "Pressure cooker" +} +item { + name: "/m/0h99cwc" + id: 551 + display_name: "Kitchen appliance" +} +item { + name: "/m/0h9mv" + id: 552 + display_name: "Tire" +} +item { + name: "/m/0hdln" + id: 553 + display_name: "Ruler" +} +item { + name: "/m/0hf58v5" + id: 554 + display_name: "Luggage and bags" +} +item { + name: "/m/0hg7b" + id: 555 + display_name: "Microphone" +} +item { + name: "/m/0hkxq" + id: 556 + display_name: "Broccoli" +} +item { + name: "/m/0hnnb" + id: 557 + display_name: "Umbrella" +} +item { + name: "/m/0hnyx" + id: 558 + display_name: "Pastry" +} +item { + name: "/m/0hqkz" + id: 559 + display_name: "Grapefruit" +} +item { + name: "/m/0j496" + id: 560 + display_name: "Band-aid" +} +item { + name: "/m/0jbk" + id: 561 + display_name: "Animal" +} +item { + name: "/m/0jg57" + id: 562 + display_name: "Bell pepper" +} +item { + name: "/m/0jly1" + id: 563 + display_name: "Turkey" +} +item { + name: "/m/0jqgx" + id: 564 + display_name: "Lily" +} +item { + name: "/m/0jwn_" + id: 565 + display_name: "Pomegranate" +} +item { + name: "/m/0jy4k" + id: 566 + display_name: "Doughnut" +} +item { + name: "/m/0jyfg" + id: 567 + display_name: "Glasses" +} +item { + name: "/m/0k0pj" + id: 568 + display_name: "Human nose" +} +item { + name: "/m/0k1tl" + id: 569 + display_name: "Pen" +} +item { + name: "/m/0_k2" + id: 570 + display_name: "Ant" +} +item { + name: "/m/0k4j" + id: 571 + display_name: "Car" +} +item { + name: "/m/0k5j" + id: 572 + display_name: "Aircraft" +} +item { + name: "/m/0k65p" + id: 573 + display_name: "Human hand" +} +item { + name: "/m/0km7z" + id: 574 + display_name: "Skunk" +} +item { + name: "/m/0kmg4" + id: 575 + display_name: "Teddy bear" +} +item { + name: "/m/0kpqd" + id: 576 + display_name: "Watermelon" +} +item { + name: "/m/0kpt_" + id: 577 + display_name: "Cantaloupe" +} +item { + name: "/m/0ky7b" + id: 578 + display_name: "Dishwasher" +} +item { + name: "/m/0l14j_" + id: 579 + display_name: "Flute" +} +item { + name: "/m/0l3ms" + id: 580 + display_name: "Balance beam" +} +item { + name: "/m/0l515" + id: 581 + display_name: "Sandwich" +} +item { + name: "/m/0ll1f78" + id: 582 + display_name: "Shrimp" +} +item { + name: "/m/0llzx" + id: 583 + display_name: "Sewing machine" +} +item { + name: "/m/0lt4_" + id: 584 + display_name: "Binoculars" +} +item { + name: "/m/0m53l" + id: 585 + display_name: "Rays and skates" +} +item { + name: "/m/0mcx2" + id: 586 + display_name: "Ipod" +} +item { + name: "/m/0mkg" + id: 587 + display_name: "Accordion" +} +item { + name: "/m/0mw_6" + id: 588 + display_name: "Willow" +} +item { + name: "/m/0n28_" + id: 589 + display_name: "Crab" +} +item { + name: "/m/0nl46" + id: 590 + display_name: "Crown" +} +item { + name: "/m/0nybt" + id: 591 + display_name: "Seahorse" +} +item { + name: "/m/0p833" + id: 592 + display_name: "Perfume" +} +item { + name: "/m/0pcr" + id: 593 + display_name: "Alpaca" +} +item { + name: "/m/0pg52" + id: 594 + display_name: "Taxi" +} +item { + name: "/m/0ph39" + id: 595 + display_name: "Canoe" +} +item { + name: "/m/0qjjc" + id: 596 + display_name: "Remote control" +} +item { + name: "/m/0qmmr" + id: 597 + display_name: "Wheelchair" +} +item { + name: "/m/0wdt60w" + id: 598 + display_name: "Rugby ball" +} +item { + name: "/m/0xfy" + id: 599 + display_name: "Armadillo" +} +item { + name: "/m/0xzly" + id: 600 + display_name: "Maracas" +} +item { + name: "/m/0zvk5" + id: 601 + display_name: "Helmet" +} diff --git a/workspace/virtuallab/object_detection/data/pascal_label_map.pbtxt b/workspace/virtuallab/object_detection/data/pascal_label_map.pbtxt new file mode 100644 index 0000000..c9e9e2a --- /dev/null +++ b/workspace/virtuallab/object_detection/data/pascal_label_map.pbtxt @@ -0,0 +1,99 @@ +item { + id: 1 + name: 'aeroplane' +} + +item { + id: 2 + name: 'bicycle' +} + +item { + id: 3 + name: 'bird' +} + +item { + id: 4 + name: 'boat' +} + +item { + id: 5 + name: 'bottle' +} + +item { + id: 6 + name: 'bus' +} + +item { + id: 7 + name: 'car' +} + +item { + id: 8 + name: 'cat' +} + +item { + id: 9 + name: 'chair' +} + +item { + id: 10 + name: 'cow' +} + +item { + id: 11 + name: 'diningtable' +} + +item { + id: 12 + name: 'dog' +} + +item { + id: 13 + name: 'horse' +} + +item { + id: 14 + name: 'motorbike' +} + +item { + id: 15 + name: 'person' +} + +item { + id: 16 + name: 'pottedplant' +} + +item { + id: 17 + name: 'sheep' +} + +item { + id: 18 + name: 'sofa' +} + +item { + id: 19 + name: 'train' +} + +item { + id: 20 + name: 'tvmonitor' +} diff --git a/workspace/virtuallab/object_detection/data/pet_label_map.pbtxt b/workspace/virtuallab/object_detection/data/pet_label_map.pbtxt new file mode 100644 index 0000000..54d7d35 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/pet_label_map.pbtxt @@ -0,0 +1,184 @@ +item { + id: 1 + name: 'Abyssinian' +} + +item { + id: 2 + name: 'american_bulldog' +} + +item { + id: 3 + name: 'american_pit_bull_terrier' +} + +item { + id: 4 + name: 'basset_hound' +} + +item { + id: 5 + name: 'beagle' +} + +item { + id: 6 + name: 'Bengal' +} + +item { + id: 7 + name: 'Birman' +} + +item { + id: 8 + name: 'Bombay' +} + +item { + id: 9 + name: 'boxer' +} + +item { + id: 10 + name: 'British_Shorthair' +} + +item { + id: 11 + name: 'chihuahua' +} + +item { + id: 12 + name: 'Egyptian_Mau' +} + +item { + id: 13 + name: 'english_cocker_spaniel' +} + +item { + id: 14 + name: 'english_setter' +} + +item { + id: 15 + name: 'german_shorthaired' +} + +item { + id: 16 + name: 'great_pyrenees' +} + +item { + id: 17 + name: 'havanese' +} + +item { + id: 18 + name: 'japanese_chin' +} + +item { + id: 19 + name: 'keeshond' +} + +item { + id: 20 + name: 'leonberger' +} + +item { + id: 21 + name: 'Maine_Coon' +} + +item { + id: 22 + name: 'miniature_pinscher' +} + +item { + id: 23 + name: 'newfoundland' +} + +item { + id: 24 + name: 'Persian' +} + +item { + id: 25 + name: 'pomeranian' +} + +item { + id: 26 + name: 'pug' +} + +item { + id: 27 + name: 'Ragdoll' +} + +item { + id: 28 + name: 'Russian_Blue' +} + +item { + id: 29 + name: 'saint_bernard' +} + +item { + id: 30 + name: 'samoyed' +} + +item { + id: 31 + name: 'scottish_terrier' +} + +item { + id: 32 + name: 'shiba_inu' +} + +item { + id: 33 + name: 'Siamese' +} + +item { + id: 34 + name: 'Sphynx' +} + +item { + id: 35 + name: 'staffordshire_bull_terrier' +} + +item { + id: 36 + name: 'wheaten_terrier' +} + +item { + id: 37 + name: 'yorkshire_terrier' +} diff --git a/workspace/virtuallab/object_detection/data/snapshot_serengeti_label_map.pbtxt b/workspace/virtuallab/object_detection/data/snapshot_serengeti_label_map.pbtxt new file mode 100644 index 0000000..57555d1 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/snapshot_serengeti_label_map.pbtxt @@ -0,0 +1,240 @@ +item { + id: 1 + name: 'human' +} + +item { + id: 2 + name: 'gazelleGrants' +} + +item { + id: 3 + name: 'reedbuck' +} + +item { + id: 4 + name: 'dikDik' +} + +item { + id: 5 + name: 'zebra' +} + +item { + id: 6 + name: 'porcupine' +} + +item { + id: 7 + name: 'gazelleThomsons' +} + +item { + id: 8 + name: 'hyenaSpotted' +} + +item { + id: 9 + name: 'warthog' +} + +item { + id: 10 + name: 'impala' +} + +item { + id: 11 + name: 'elephant' +} + +item { + id: 12 + name: 'giraffe' +} + +item { + id: 13 + name: 'mongoose' +} + +item { + id: 14 + name: 'buffalo' +} + +item { + id: 15 + name: 'hartebeest' +} + +item { + id: 16 + name: 'guineaFowl' +} + +item { + id: 17 + name: 'wildebeest' +} + +item { + id: 18 + name: 'leopard' +} + +item { + id: 19 + name: 'ostrich' +} + +item { + id: 20 + name: 'lionFemale' +} + +item { + id: 21 + name: 'koriBustard' +} + +item { + id: 22 + name: 'otherBird' +} + +item { + id: 23 + name: 'batEaredFox' +} + +item { + id: 24 + name: 'bushbuck' +} + +item { + id: 25 + name: 'jackal' +} + +item { + id: 26 + name: 'cheetah' +} + +item { + id: 27 + name: 'eland' +} + +item { + id: 28 + name: 'aardwolf' +} + +item { + id: 29 + name: 'hippopotamus' +} + +item { + id: 30 + name: 'hyenaStriped' +} + +item { + id: 31 + name: 'aardvark' +} + +item { + id: 32 + name: 'hare' +} + +item { + id: 33 + name: 'baboon' +} + +item { + id: 34 + name: 'vervetMonkey' +} + +item { + id: 35 + name: 'waterbuck' +} + +item { + id: 36 + name: 'secretaryBird' +} + +item { + id: 37 + name: 'serval' +} + +item { + id: 38 + name: 'lionMale' +} + +item { + id: 39 + name: 'topi' +} + +item { + id: 40 + name: 'honeyBadger' +} + +item { + id: 41 + name: 'rodents' +} + +item { + id: 42 + name: 'wildcat' +} + +item { + id: 43 + name: 'civet' +} + +item { + id: 44 + name: 'genet' +} + +item { + id: 45 + name: 'caracal' +} + +item { + id: 46 + name: 'rhinoceros' +} + +item { + id: 47 + name: 'reptiles' +} + +item { + id: 48 + name: 'zorilla' +} + diff --git a/workspace/virtuallab/object_detection/data/test_labels.csv b/workspace/virtuallab/object_detection/data/test_labels.csv new file mode 100644 index 0000000..04b242c --- /dev/null +++ b/workspace/virtuallab/object_detection/data/test_labels.csv @@ -0,0 +1,121 @@ +filename,width,height,class,xmin,ymin,xmax,ymax +frame1035.jpg,800,800,myrobot,272,232,352,442 +frame1035.jpg,800,800,corobot,1,166,106,379 +frame1035.jpg,800,800,corobot,171,54,257,296 +frame1035.jpg,800,800,corobot,453,95,547,320 +frame1035.jpg,800,800,corobot,200,375,282,540 +frame1035.jpg,800,800,corobot,407,487,504,645 +frame4045.jpg,800,800,corobot,134,1,224,232 +frame4045.jpg,800,800,corobot,79,386,177,558 +frame4045.jpg,800,800,corobot,357,146,437,358 +frame4045.jpg,800,800,corobot,384,414,469,581 +frame4045.jpg,800,800,corobot,493,299,592,480 +frame4045.jpg,800,800,myrobot,267,232,348,445 +frame2035.jpg,800,800,corobot,113,50,211,286 +frame2035.jpg,800,800,corobot,80,352,178,524 +frame2035.jpg,800,800,corobot,194,514,280,661 +frame2035.jpg,800,800,corobot,382,315,473,500 +frame2035.jpg,800,800,corobot,489,113,598,322 +frame2035.jpg,800,800,myrobot,268,263,341,446 +frame2040.jpg,800,800,corobot,110,44,210,285 +frame2040.jpg,800,800,corobot,79,347,180,531 +frame2040.jpg,800,800,corobot,382,309,480,500 +frame2040.jpg,800,800,corobot,202,498,295,651 +frame2040.jpg,800,800,corobot,495,109,604,330 +frame2040.jpg,800,800,myrobot,267,256,341,446 +frame1040.jpg,800,800,corobot,7,182,121,392 +frame1040.jpg,800,800,corobot,162,59,257,303 +frame1040.jpg,800,800,corobot,201,372,285,542 +frame1040.jpg,800,800,corobot,445,96,548,323 +frame1040.jpg,800,800,corobot,399,495,494,651 +frame1040.jpg,800,800,myrobot,268,235,353,447 +frame2045.jpg,800,800,corobot,146,85,240,310 +frame2045.jpg,800,800,corobot,110,389,207,559 +frame2045.jpg,800,800,corobot,238,465,332,621 +frame2045.jpg,800,800,corobot,351,350,442,532 +frame2045.jpg,800,800,corobot,514,137,630,354 +frame2045.jpg,800,800,myrobot,264,256,344,446 +frame3045.jpg,800,800,corobot,1,359,100,530 +frame3045.jpg,800,800,corobot,386,409,470,570 +frame3045.jpg,800,800,corobot,413,102,498,325 +frame3045.jpg,800,800,corobot,181,135,265,351 +frame3045.jpg,800,800,corobot,292,21,367,259 +frame3045.jpg,800,800,myrobot,274,264,346,444 +frame3050.jpg,800,800,corobot,2,366,102,534 +frame3050.jpg,800,800,corobot,388,414,470,570 +frame3050.jpg,800,800,corobot,184,132,268,347 +frame3050.jpg,800,800,corobot,410,103,498,324 +frame3050.jpg,800,800,corobot,304,18,380,256 +frame3050.jpg,800,800,myrobot,276,259,346,445 +frame1050.jpg,800,800,corobot,130,87,226,320 +frame1050.jpg,800,800,corobot,32,208,143,412 +frame1050.jpg,800,800,corobot,429,121,520,346 +frame1050.jpg,800,800,corobot,388,513,479,662 +frame1050.jpg,800,800,corobot,159,329,251,515 +frame1050.jpg,800,800,myrobot,273,229,354,450 +frame1000.jpg,800,800,corobot,2,166,108,381 +frame1000.jpg,800,800,corobot,202,64,282,312 +frame1000.jpg,800,800,corobot,187,416,270,566 +frame1000.jpg,800,800,corobot,438,77,541,298 +frame1000.jpg,800,800,corobot,430,491,523,653 +frame1000.jpg,800,800,myrobot,270,235,350,446 +frame4025.jpg,800,800,myrobot,266,235,351,452 +frame4025.jpg,800,800,corobot,139,4,236,247 +frame4025.jpg,800,800,corobot,75,400,180,556 +frame4025.jpg,800,800,corobot,348,158,429,360 +frame4025.jpg,800,800,corobot,381,408,471,588 +frame4025.jpg,800,800,corobot,497,296,610,480 +frame3035.jpg,800,800,corobot,13,396,126,556 +frame3035.jpg,800,800,corobot,414,443,503,598 +frame3035.jpg,800,800,corobot,142,166,234,381 +frame3035.jpg,800,800,corobot,440,149,536,358 +frame3035.jpg,800,800,corobot,338,2,418,224 +frame3035.jpg,800,800,myrobot,275,238,344,446 +frame4050.jpg,800,800,corobot,102,14,193,251 +frame4050.jpg,800,800,corobot,54,404,153,568 +frame4050.jpg,800,800,corobot,344,122,418,337 +frame4050.jpg,800,800,corobot,364,432,445,596 +frame4050.jpg,800,800,corobot,511,330,618,501 +frame4050.jpg,800,800,myrobot,270,235,342,442 +frame2051.jpg,800,800,corobot,144,72,235,309 +frame2051.jpg,800,800,corobot,107,380,207,552 +frame2051.jpg,800,800,corobot,353,350,448,527 +frame2051.jpg,800,800,corobot,222,476,317,635 +frame2051.jpg,800,800,corobot,510,117,623,343 +frame2051.jpg,800,800,myrobot,265,259,347,448 +frame3040.jpg,800,800,corobot,12,391,121,552 +frame3040.jpg,800,800,corobot,412,446,503,598 +frame3040.jpg,800,800,corobot,440,147,531,357 +frame3040.jpg,800,800,corobot,137,173,227,382 +frame3040.jpg,800,800,corobot,326,1,405,232 +frame3040.jpg,800,800,myrobot,274,246,344,445 +frame1045.jpg,800,800,myrobot,267,232,354,448 +frame1045.jpg,800,800,corobot,45,218,148,421 +frame1045.jpg,800,800,corobot,126,86,219,322 +frame1045.jpg,800,800,corobot,163,331,251,509 +frame1045.jpg,800,800,corobot,414,129,516,351 +frame1045.jpg,800,800,corobot,376,516,471,668 +frame2025.jpg,800,800,corobot,162,53,254,298 +frame2025.jpg,800,800,corobot,497,160,609,373 +frame2025.jpg,800,800,corobot,89,416,194,571 +frame2025.jpg,800,800,corobot,344,317,438,512 +frame2025.jpg,800,800,corobot,247,478,342,635 +frame2025.jpg,800,800,myrobot,265,253,348,448 +frame2050.jpg,800,800,corobot,142,72,237,309 +frame2050.jpg,800,800,corobot,109,382,208,558 +frame2050.jpg,800,800,corobot,226,475,321,632 +frame2050.jpg,800,800,corobot,348,354,442,535 +frame2050.jpg,800,800,corobot,510,121,629,347 +frame2050.jpg,800,800,myrobot,265,257,346,443 +frame4040.jpg,800,800,corobot,169,1,256,219 +frame4040.jpg,800,800,corobot,109,357,206,535 +frame4040.jpg,800,800,corobot,368,164,446,375 +frame4040.jpg,800,800,corobot,476,289,573,428 +frame4040.jpg,800,800,corobot,412,436,498,555 +frame4040.jpg,800,800,myrobot,270,240,342,446 +frame4035.jpg,800,800,corobot,134,1,225,235 +frame4035.jpg,800,800,corobot,89,373,188,547 +frame4035.jpg,800,800,corobot,352,152,428,363 +frame4035.jpg,800,800,corobot,502,309,601,481 +frame4035.jpg,800,800,corobot,386,412,479,581 +frame4035.jpg,800,800,myrobot,266,237,347,446 diff --git a/workspace/virtuallab/object_detection/data/train_labels.csv b/workspace/virtuallab/object_detection/data/train_labels.csv new file mode 100644 index 0000000..663fb16 --- /dev/null +++ b/workspace/virtuallab/object_detection/data/train_labels.csv @@ -0,0 +1,577 @@ +filename,width,height,class,xmin,ymin,xmax,ymax +frame2030.jpg,800,800,myrobot,270,251,341,446 +frame2030.jpg,800,800,corobot,150,74,241,315 +frame2030.jpg,800,800,corobot,509,141,618,362 +frame2030.jpg,800,800,corobot,109,401,204,560 +frame2030.jpg,800,800,corobot,341,337,427,522 +frame2030.jpg,800,800,corobot,235,483,323,637 +frame2028.jpg,800,800,myrobot,270,254,339,446 +frame2028.jpg,800,800,corobot,343,329,428,518 +frame2028.jpg,800,800,corobot,498,147,611,355 +frame2028.jpg,800,800,corobot,94,396,199,559 +frame2028.jpg,800,800,corobot,239,482,327,637 +frame2028.jpg,800,800,corobot,148,65,240,309 +frame2038.jpg,800,800,corobot,117,52,216,292 +frame2038.jpg,800,800,corobot,81,359,186,533 +frame2038.jpg,800,800,corobot,202,508,289,655 +frame2038.jpg,800,800,corobot,385,312,479,501 +frame2038.jpg,800,800,corobot,498,118,604,337 +frame2038.jpg,800,800,myrobot,269,257,345,445 +frame2036.jpg,800,800,corobot,155,95,251,320 +frame2036.jpg,800,800,corobot,115,403,213,568 +frame2036.jpg,800,800,corobot,243,475,328,628 +frame2036.jpg,800,800,corobot,339,349,429,530 +frame2036.jpg,800,800,corobot,519,164,637,371 +frame2036.jpg,800,800,myrobot,270,258,343,443 +frame1031.jpg,800,800,myrobot,271,228,352,450 +frame1031.jpg,800,800,corobot,24,209,131,414 +frame1031.jpg,800,800,corobot,125,76,221,316 +frame1031.jpg,800,800,corobot,425,136,521,350 +frame1031.jpg,800,800,corobot,180,341,266,515 +frame1031.jpg,800,800,corobot,376,506,472,660 +frame2032.jpg,800,800,myrobot,268,261,339,446 +frame2032.jpg,800,800,corobot,112,45,205,284 +frame2032.jpg,800,800,corobot,485,96,592,325 +frame2032.jpg,800,800,corobot,80,355,181,525 +frame2032.jpg,800,800,corobot,391,308,483,499 +frame2032.jpg,800,800,corobot,195,514,288,664 +frame3034.jpg,800,800,corobot,4,378,116,550 +frame3034.jpg,800,800,corobot,393,420,480,580 +frame3034.jpg,800,800,corobot,163,152,253,373 +frame3034.jpg,800,800,corobot,425,119,514,337 +frame3034.jpg,800,800,corobot,316,3,394,241 +frame3034.jpg,800,800,myrobot,272,246,345,446 +frame1036.jpg,800,800,corobot,35,217,143,419 +frame1036.jpg,800,800,corobot,112,87,211,323 +frame1036.jpg,800,800,corobot,412,144,505,354 +frame1036.jpg,800,800,corobot,165,322,251,497 +frame1036.jpg,800,800,corobot,356,525,444,673 +frame1036.jpg,800,800,myrobot,271,232,350,446 +frame3024.jpg,800,800,corobot,160,152,248,385 +frame3024.jpg,800,800,corobot,1,383,124,542 +frame3024.jpg,800,800,corobot,312,1,388,242 +frame3024.jpg,800,800,corobot,424,141,520,353 +frame3024.jpg,800,800,corobot,399,424,489,596 +frame3024.jpg,800,800,myrobot,275,250,344,448 +frame3037.jpg,800,800,corobot,3,375,116,543 +frame3037.jpg,800,800,corobot,405,431,494,591 +frame3037.jpg,800,800,corobot,150,162,243,378 +frame3037.jpg,800,800,corobot,432,137,521,346 +frame3037.jpg,800,800,corobot,326,1,403,231 +frame3037.jpg,800,800,myrobot,275,242,344,445 +frame1051.jpg,800,800,myrobot,272,231,354,444 +frame1051.jpg,800,800,corobot,31,200,139,405 +frame1051.jpg,800,800,corobot,131,82,231,315 +frame1051.jpg,800,800,corobot,429,123,523,354 +frame1051.jpg,800,800,corobot,167,335,256,516 +frame1051.jpg,800,800,corobot,386,514,483,664 +frame3039.jpg,800,800,corobot,1,339,94,514 +frame3039.jpg,800,800,corobot,379,402,461,564 +frame3039.jpg,800,800,corobot,408,100,497,317 +frame3039.jpg,800,800,corobot,179,144,267,355 +frame3039.jpg,800,800,corobot,289,18,367,257 +frame3039.jpg,800,800,myrobot,273,262,344,444 +frame3047.jpg,800,800,corobot,2,378,108,542 +frame3047.jpg,800,800,corobot,393,425,479,580 +frame3047.jpg,800,800,corobot,421,119,507,337 +frame3047.jpg,800,800,corobot,171,146,257,353 +frame3047.jpg,800,800,corobot,303,15,380,256 +frame3047.jpg,800,800,myrobot,276,259,344,446 +frame3043.jpg,800,800,corobot,22,404,130,566 +frame3043.jpg,800,800,corobot,424,455,514,610 +frame3043.jpg,800,800,corobot,447,159,542,366 +frame3043.jpg,800,800,corobot,138,168,230,380 +frame3043.jpg,800,800,corobot,339,1,419,231 +frame3043.jpg,800,800,myrobot,275,241,346,445 +frame4038.jpg,800,800,corobot,130,2,221,241 +frame4038.jpg,800,800,corobot,73,389,174,555 +frame4038.jpg,800,800,corobot,342,135,418,340 +frame4038.jpg,800,800,corobot,376,424,463,587 +frame4038.jpg,800,800,corobot,509,318,617,495 +frame4038.jpg,800,800,myrobot,267,240,342,445 +frame2042.jpg,800,800,corobot,144,82,238,311 +frame2042.jpg,800,800,corobot,518,141,635,359 +frame2042.jpg,800,800,corobot,107,389,207,556 +frame2042.jpg,800,800,corobot,346,356,435,532 +frame2042.jpg,800,800,corobot,242,469,328,626 +frame2042.jpg,800,800,myrobot,267,255,344,444 +frame3051.jpg,800,800,corobot,2,372,105,539 +frame3051.jpg,800,800,corobot,392,417,473,576 +frame3051.jpg,800,800,corobot,415,113,502,329 +frame3051.jpg,800,800,corobot,170,145,260,355 +frame3051.jpg,800,800,corobot,313,10,388,252 +frame3051.jpg,800,800,myrobot,274,258,344,445 +frame1028.jpg,800,800,corobot,25,220,134,420 +frame1028.jpg,800,800,corobot,130,73,221,317 +frame1028.jpg,800,800,corobot,180,330,266,503 +frame1028.jpg,800,800,corobot,434,145,531,350 +frame1028.jpg,800,800,corobot,378,494,469,655 +frame1028.jpg,800,800,myrobot,271,230,353,447 +frame4047.jpg,800,800,corobot,98,4,192,253 +frame4047.jpg,800,800,corobot,51,402,151,565 +frame4047.jpg,800,800,corobot,335,115,413,299 +frame4047.jpg,800,800,myrobot,269,253,344,441 +frame4047.jpg,800,800,corobot,512,327,615,492 +frame4047.jpg,800,800,corobot,356,432,439,586 +frame4049.jpg,800,800,corobot,141,1,226,228 +frame4049.jpg,800,800,corobot,373,162,453,368 +frame4049.jpg,800,800,corobot,94,373,187,542 +frame4049.jpg,800,800,corobot,481,285,576,458 +frame4049.jpg,800,800,corobot,392,401,480,568 +frame4049.jpg,800,800,myrobot,268,238,345,441 +frame3046.jpg,800,800,corobot,27,414,135,572 +frame3046.jpg,800,800,corobot,426,460,514,612 +frame3046.jpg,800,800,corobot,448,153,538,367 +frame3046.jpg,800,800,corobot,132,171,221,382 +frame3046.jpg,800,800,corobot,339,2,419,234 +frame3046.jpg,800,800,myrobot,276,250,344,443 +frame3031.jpg,800,800,corobot,1,363,112,532 +frame3031.jpg,800,800,corobot,384,415,473,576 +frame3031.jpg,800,800,corobot,174,148,265,368 +frame3031.jpg,800,800,corobot,417,109,509,327 +frame3031.jpg,800,800,corobot,304,2,382,247 +frame3031.jpg,800,800,myrobot,276,254,345,446 +frame3000.jpg,800,800,corobot,344,1,428,234 +frame3000.jpg,800,800,corobot,112,152,215,384 +frame3000.jpg,800,800,corobot,1,433,125,582 +frame3000.jpg,800,800,corobot,428,193,526,394 +frame3000.jpg,800,800,corobot,440,422,542,600 +frame3000.jpg,800,800,myrobot,273,246,355,444 +frame4030.jpg,800,800,corobot,57,422,167,572 +frame4030.jpg,800,800,corobot,354,425,438,590 +frame4030.jpg,800,800,corobot,521,324,629,502 +frame4030.jpg,800,800,corobot,94,5,191,252 +frame4030.jpg,800,800,corobot,343,121,419,326 +frame4030.jpg,800,800,myrobot,270,236,341,445 +frame3042.jpg,800,800,corobot,1,351,96,527 +frame3042.jpg,800,800,corobot,385,410,470,570 +frame3042.jpg,800,800,corobot,417,109,501,323 +frame3042.jpg,800,800,corobot,184,133,269,350 +frame3042.jpg,800,800,corobot,293,21,368,260 +frame3042.jpg,800,800,myrobot,276,263,344,446 +frame3028.jpg,800,800,corobot,3,368,121,533 +frame3028.jpg,800,800,corobot,380,413,471,588 +frame3028.jpg,800,800,corobot,189,147,275,369 +frame3028.jpg,800,800,corobot,422,102,513,330 +frame3028.jpg,800,800,corobot,293,5,371,249 +frame3028.jpg,800,800,myrobot,280,254,345,443 +frame2046.jpg,800,800,corobot,102,34,203,274 +frame2046.jpg,800,800,corobot,480,83,587,310 +frame2046.jpg,800,800,corobot,71,332,177,518 +frame2046.jpg,800,800,corobot,394,306,484,497 +frame2046.jpg,800,800,corobot,189,502,286,656 +frame2046.jpg,800,800,myrobot,267,255,342,446 +frame4048.jpg,800,800,corobot,135,1,223,230 +frame4048.jpg,800,800,corobot,91,375,185,543 +frame4048.jpg,800,800,corobot,363,150,445,367 +frame4048.jpg,800,800,corobot,483,283,584,464 +frame4048.jpg,800,800,corobot,395,403,474,569 +frame4048.jpg,800,800,myrobot,269,238,344,441 +frame4026.jpg,800,800,corobot,162,1,256,246 +frame4026.jpg,800,800,corobot,71,361,181,523 +frame4026.jpg,800,800,corobot,349,187,428,382 +frame4026.jpg,800,800,corobot,484,267,590,453 +frame4026.jpg,800,800,corobot,411,445,503,583 +frame4026.jpg,800,800,myrobot,265,241,349,446 +frame3026.jpg,800,800,corobot,3,431,123,586 +frame3026.jpg,800,800,corobot,436,423,526,597 +frame3026.jpg,800,800,corobot,429,179,526,387 +frame3026.jpg,800,800,corobot,118,154,208,384 +frame3026.jpg,800,800,corobot,334,1,416,228 +frame3026.jpg,800,800,myrobot,268,230,345,443 +frame1046.jpg,800,800,corobot,1,169,119,381 +frame1046.jpg,800,800,corobot,176,52,269,296 +frame1046.jpg,800,800,corobot,462,86,562,317 +frame1046.jpg,800,800,corobot,203,379,289,550 +frame1046.jpg,800,800,corobot,424,478,524,634 +frame1046.jpg,800,800,myrobot,271,234,353,445 +frame2037.jpg,800,800,corobot,121,56,222,293 +frame2037.jpg,800,800,corobot,85,364,190,537 +frame2037.jpg,800,800,corobot,373,318,467,510 +frame2037.jpg,800,800,corobot,200,505,298,651 +frame2037.jpg,800,800,corobot,503,123,615,344 +frame2037.jpg,800,800,myrobot,267,257,345,449 +frame2041.jpg,800,800,corobot,118,52,211,288 +frame2041.jpg,800,800,corobot,82,358,184,532 +frame2041.jpg,800,800,corobot,211,492,302,647 +frame2041.jpg,800,800,corobot,380,323,467,507 +frame2041.jpg,800,800,corobot,498,109,609,336 +frame2041.jpg,800,800,myrobot,267,255,343,447 +frame4037.jpg,800,800,corobot,172,1,260,216 +frame4037.jpg,800,800,corobot,370,174,449,376 +frame4037.jpg,800,800,corobot,114,356,207,528 +frame4037.jpg,800,800,corobot,484,277,581,422 +frame4037.jpg,800,800,corobot,417,424,504,562 +frame4037.jpg,800,800,myrobot,267,237,348,436 +frame1032.jpg,800,800,corobot,167,56,259,302 +frame1032.jpg,800,800,corobot,1,171,107,385 +frame1032.jpg,800,800,corobot,451,97,548,320 +frame1032.jpg,800,800,corobot,205,387,287,549 +frame1032.jpg,800,800,corobot,417,481,514,642 +frame1032.jpg,800,800,myrobot,271,231,351,448 +frame3049.jpg,800,800,corobot,28,413,134,573 +frame3049.jpg,800,800,corobot,424,461,511,608 +frame3049.jpg,800,800,corobot,444,153,534,363 +frame3049.jpg,800,800,corobot,139,164,230,378 +frame3049.jpg,800,800,corobot,344,2,422,230 +frame3049.jpg,800,800,myrobot,275,246,345,449 +frame1042.jpg,800,800,corobot,41,215,147,421 +frame1042.jpg,800,800,corobot,121,85,228,319 +frame1042.jpg,800,800,corobot,161,327,253,505 +frame1042.jpg,800,800,corobot,419,131,512,351 +frame1042.jpg,800,800,corobot,370,518,461,669 +frame1042.jpg,800,800,myrobot,269,233,352,446 +frame1029.jpg,800,800,myrobot,271,232,351,446 +frame1029.jpg,800,800,corobot,1,161,106,379 +frame1029.jpg,800,800,corobot,181,53,270,298 +frame1029.jpg,800,800,corobot,452,90,554,304 +frame1029.jpg,800,800,corobot,200,391,282,550 +frame1029.jpg,800,800,corobot,433,481,528,642 +frame2031.jpg,800,800,corobot,142,78,235,314 +frame2031.jpg,800,800,corobot,507,144,620,356 +frame2031.jpg,800,800,corobot,104,396,200,559 +frame2031.jpg,800,800,corobot,348,328,439,519 +frame2031.jpg,800,800,corobot,230,487,316,643 +frame2031.jpg,800,800,myrobot,266,259,340,446 +frame1038.jpg,800,800,corobot,5,173,116,388 +frame1038.jpg,800,800,corobot,158,60,251,301 +frame1038.jpg,800,800,corobot,444,100,540,328 +frame1038.jpg,800,800,corobot,203,374,287,541 +frame1038.jpg,800,800,corobot,396,497,494,651 +frame1038.jpg,800,800,myrobot,270,233,354,445 +frame4052.jpg,800,800,corobot,140,1,228,225 +frame4052.jpg,800,800,corobot,368,153,450,366 +frame4052.jpg,800,800,corobot,100,368,191,537 +frame4052.jpg,800,800,corobot,401,403,486,565 +frame4052.jpg,800,800,corobot,491,298,587,471 +frame4052.jpg,800,800,myrobot,270,237,349,446 +frame3027.jpg,800,800,corobot,2,384,121,548 +frame3027.jpg,800,800,corobot,389,417,478,590 +frame3027.jpg,800,800,corobot,172,148,262,377 +frame3027.jpg,800,800,corobot,419,129,516,341 +frame3027.jpg,800,800,corobot,297,4,380,249 +frame3027.jpg,800,800,myrobot,275,253,344,450 +frame2048.jpg,800,800,corobot,139,70,234,305 +frame2048.jpg,800,800,corobot,507,124,620,343 +frame2048.jpg,800,800,corobot,105,382,205,551 +frame2048.jpg,800,800,corobot,349,354,438,538 +frame2048.jpg,800,800,corobot,233,470,321,628 +frame2048.jpg,800,800,myrobot,268,253,344,444 +frame1025.jpg,800,800,corobot,118,64,214,312 +frame1025.jpg,800,800,corobot,30,232,139,429 +frame1025.jpg,800,800,corobot,187,341,274,508 +frame1025.jpg,800,800,corobot,440,159,543,362 +frame1025.jpg,800,800,corobot,368,491,452,652 +frame1025.jpg,800,800,myrobot,273,231,353,446 +frame3038.jpg,800,800,corobot,7,383,118,551 +frame3038.jpg,800,800,corobot,411,439,496,591 +frame3038.jpg,800,800,corobot,139,171,230,383 +frame3038.jpg,800,800,corobot,435,145,527,351 +frame3038.jpg,800,800,corobot,331,2,411,230 +frame3038.jpg,800,800,myrobot,274,243,346,445 +frame1026.jpg,800,800,corobot,2,174,110,383 +frame1026.jpg,800,800,corobot,192,63,276,310 +frame1026.jpg,800,800,corobot,444,87,547,308 +frame1026.jpg,800,800,corobot,191,411,275,564 +frame1026.jpg,800,800,corobot,436,490,529,652 +frame1026.jpg,800,800,myrobot,274,236,350,444 +frame3041.jpg,800,800,corobot,8,385,118,553 +frame3041.jpg,800,800,corobot,411,436,494,594 +frame3041.jpg,800,800,corobot,151,161,237,376 +frame3041.jpg,800,800,corobot,435,146,525,351 +frame3041.jpg,800,800,corobot,320,4,398,241 +frame3041.jpg,800,800,myrobot,276,250,344,442 +frame2044.jpg,800,800,corobot,124,52,217,296 +frame2044.jpg,800,800,corobot,91,365,190,536 +frame2044.jpg,800,800,corobot,212,490,307,641 +frame2044.jpg,800,800,corobot,368,334,460,522 +frame2044.jpg,800,800,corobot,501,119,616,340 +frame2044.jpg,800,800,myrobot,267,255,344,446 +frame1044.jpg,800,800,corobot,151,60,246,305 +frame1044.jpg,800,800,corobot,27,189,129,403 +frame1044.jpg,800,800,corobot,444,103,539,332 +frame1044.jpg,800,800,corobot,182,352,268,528 +frame1044.jpg,800,800,corobot,393,501,489,655 +frame1044.jpg,800,800,myrobot,269,232,354,447 +frame3036.jpg,800,800,corobot,1,337,94,510 +frame3036.jpg,800,800,corobot,375,396,459,562 +frame3036.jpg,800,800,corobot,408,91,496,318 +frame3036.jpg,800,800,corobot,186,132,276,352 +frame3036.jpg,800,800,corobot,294,14,370,255 +frame3036.jpg,800,800,myrobot,276,263,344,445 +frame1030.jpg,800,800,corobot,22,210,131,413 +frame1030.jpg,800,800,corobot,121,76,219,323 +frame1030.jpg,800,800,corobot,424,132,517,346 +frame1030.jpg,800,800,corobot,175,331,263,508 +frame1030.jpg,800,800,corobot,377,507,470,659 +frame1030.jpg,800,800,myrobot,270,228,352,445 +frame3033.jpg,800,800,corobot,1,350,102,521 +frame3033.jpg,800,800,corobot,371,395,457,562 +frame3033.jpg,800,800,corobot,407,88,496,312 +frame3033.jpg,800,800,corobot,188,145,274,359 +frame3033.jpg,800,800,corobot,288,15,364,255 +frame3033.jpg,800,800,myrobot,277,260,344,446 +frame2033.jpg,800,800,corobot,159,88,249,322 +frame2033.jpg,800,800,corobot,116,409,210,570 +frame2033.jpg,800,800,corobot,246,473,333,630 +frame2033.jpg,800,800,corobot,341,344,426,527 +frame2033.jpg,800,800,corobot,514,155,628,361 +frame2033.jpg,800,800,myrobot,268,259,337,442 +frame2034.jpg,800,800,corobot,135,67,226,304 +frame2034.jpg,800,800,corobot,94,380,194,549 +frame2034.jpg,800,800,corobot,361,329,453,514 +frame2034.jpg,800,800,corobot,217,496,306,650 +frame2034.jpg,800,800,corobot,502,122,609,341 +frame2034.jpg,800,800,myrobot,270,255,340,444 +frame4041.jpg,800,800,corobot,120,1,212,246 +frame4041.jpg,800,800,corobot,336,132,411,300 +frame4041.jpg,800,800,corobot,67,398,163,564 +frame4041.jpg,800,800,corobot,365,432,454,592 +frame4041.jpg,800,800,corobot,505,323,606,491 +frame4041.jpg,800,800,myrobot,261,285,344,439 +frame1037.jpg,800,800,myrobot,269,235,354,446 +frame1037.jpg,800,800,corobot,2,183,116,393 +frame1037.jpg,800,800,corobot,152,68,246,302 +frame1037.jpg,800,800,corobot,441,105,539,334 +frame1037.jpg,800,800,corobot,193,364,280,536 +frame1037.jpg,800,800,corobot,390,501,485,653 +frame4033.jpg,800,800,corobot,106,1,203,249 +frame4033.jpg,800,800,corobot,67,402,169,565 +frame4033.jpg,800,800,corobot,335,124,412,319 +frame4033.jpg,800,800,corobot,362,424,454,594 +frame4033.jpg,800,800,corobot,513,323,625,502 +frame4033.jpg,800,800,myrobot,267,288,342,450 +frame3048.jpg,800,800,corobot,2,373,107,540 +frame3048.jpg,800,800,corobot,394,419,476,578 +frame3048.jpg,800,800,corobot,417,114,504,333 +frame3048.jpg,800,800,corobot,181,135,267,350 +frame3048.jpg,800,800,corobot,299,19,378,257 +frame3048.jpg,800,800,myrobot,275,263,344,443 +frame4034.jpg,800,800,corobot,162,1,255,226 +frame4034.jpg,800,800,corobot,112,350,207,527 +frame4034.jpg,800,800,corobot,361,173,449,382 +frame4034.jpg,800,800,corobot,488,273,584,428 +frame4034.jpg,800,800,corobot,417,423,500,560 +frame4034.jpg,800,800,myrobot,270,241,346,445 +frame3029.jpg,800,800,corobot,22,423,134,583 +frame3029.jpg,800,800,corobot,426,437,521,605 +frame3029.jpg,800,800,corobot,134,161,225,392 +frame3029.jpg,800,800,corobot,339,1,423,227 +frame3029.jpg,800,800,corobot,437,167,537,371 +frame3029.jpg,800,800,myrobot,274,235,344,444 +frame1024.jpg,800,800,corobot,7,197,123,408 +frame1024.jpg,800,800,corobot,162,63,246,307 +frame1024.jpg,800,800,corobot,187,373,273,535 +frame1024.jpg,800,800,corobot,441,123,542,332 +frame1024.jpg,800,800,corobot,394,491,481,652 +frame1024.jpg,800,800,myrobot,268,226,350,446 +frame4036.jpg,800,800,corobot,117,1,212,248 +frame4036.jpg,800,800,corobot,67,402,166,559 +frame4036.jpg,800,800,corobot,342,131,414,336 +frame4036.jpg,800,800,corobot,365,431,455,591 +frame4036.jpg,800,800,corobot,517,329,623,502 +frame4036.jpg,800,800,myrobot,264,240,346,446 +frame3025.jpg,800,800,corobot,3,360,123,529 +frame3025.jpg,800,800,corobot,365,422,452,596 +frame3025.jpg,800,800,corobot,423,107,525,326 +frame3025.jpg,800,800,corobot,279,5,363,257 +frame3025.jpg,800,800,corobot,189,150,271,379 +frame3025.jpg,800,800,myrobot,275,259,345,447 +frame4031.jpg,800,800,corobot,153,1,243,234 +frame4031.jpg,800,800,corobot,100,361,198,531 +frame4031.jpg,800,800,corobot,362,175,442,385 +frame4031.jpg,800,800,corobot,492,285,593,460 +frame4031.jpg,800,800,corobot,408,400,495,562 +frame4031.jpg,800,800,myrobot,266,233,345,447 +frame4029.jpg,800,800,corobot,86,370,186,537 +frame4029.jpg,800,800,corobot,152,4,237,241 +frame4029.jpg,800,800,corobot,357,181,438,379 +frame4029.jpg,800,800,corobot,496,278,589,454 +frame4029.jpg,800,800,corobot,392,404,488,580 +frame4029.jpg,800,800,myrobot,271,234,345,448 +frame1043.jpg,800,800,myrobot,269,232,354,446 +frame1043.jpg,800,800,corobot,4,172,116,384 +frame1043.jpg,800,800,corobot,179,52,267,292 +frame1043.jpg,800,800,corobot,459,86,558,317 +frame1043.jpg,800,800,corobot,196,370,282,541 +frame1043.jpg,800,800,corobot,413,483,511,640 +frame1034.jpg,800,800,corobot,9,191,123,403 +frame1034.jpg,800,800,corobot,141,75,232,313 +frame1034.jpg,800,800,corobot,433,119,523,341 +frame1034.jpg,800,800,corobot,186,350,270,522 +frame1034.jpg,800,800,corobot,394,500,485,654 +frame1034.jpg,800,800,myrobot,270,235,351,444 +frame4042.jpg,800,800,corobot,126,2,213,243 +frame4042.jpg,800,800,corobot,346,137,422,341 +frame4042.jpg,800,800,corobot,71,392,171,557 +frame4042.jpg,800,800,corobot,372,422,458,586 +frame4042.jpg,800,800,corobot,500,306,595,483 +frame4042.jpg,800,800,myrobot,262,240,344,441 +frame3044.jpg,800,800,corobot,5,380,113,545 +frame3044.jpg,800,800,corobot,406,434,493,591 +frame3044.jpg,800,800,corobot,430,133,520,346 +frame3044.jpg,800,800,corobot,160,154,245,368 +frame3044.jpg,800,800,corobot,316,5,394,241 +frame3044.jpg,800,800,myrobot,275,251,345,441 +frame4028.jpg,800,800,corobot,80,379,185,538 +frame4028.jpg,800,800,corobot,145,2,233,242 +frame4028.jpg,800,800,corobot,398,437,484,578 +frame4028.jpg,800,800,corobot,492,278,595,464 +frame4028.jpg,800,800,corobot,355,170,436,370 +frame4028.jpg,800,800,myrobot,269,233,346,446 +frame4044.jpg,800,800,corobot,107,2,201,250 +frame4044.jpg,800,800,corobot,343,125,414,323 +frame4044.jpg,800,800,myrobot,267,241,344,442 +frame4044.jpg,800,800,corobot,353,436,442,596 +frame4044.jpg,800,800,corobot,515,323,617,491 +frame4044.jpg,800,800,corobot,53,409,157,570 +frame4032.jpg,800,800,corobot,145,1,238,234 +frame4032.jpg,800,800,corobot,91,372,192,541 +frame4032.jpg,800,800,corobot,355,163,434,371 +frame4032.jpg,800,800,corobot,494,296,595,474 +frame4032.jpg,800,800,corobot,397,402,490,577 +frame4032.jpg,800,800,myrobot,267,237,347,449 +frame2024.jpg,800,800,myrobot,265,250,341,449 +frame2024.jpg,800,800,corobot,127,52,222,301 +frame2024.jpg,800,800,corobot,493,140,610,346 +frame2024.jpg,800,800,corobot,88,379,193,538 +frame2024.jpg,800,800,corobot,225,494,314,646 +frame2024.jpg,800,800,corobot,367,318,455,516 +frame4043.jpg,800,800,corobot,160,1,244,218 +frame4043.jpg,800,800,corobot,102,364,197,541 +frame4043.jpg,800,800,corobot,372,165,455,373 +frame4043.jpg,800,800,corobot,477,276,576,423 +frame4043.jpg,800,800,corobot,402,422,489,568 +frame4043.jpg,800,800,myrobot,269,239,345,441 +frame1047.jpg,800,800,myrobot,271,228,355,445 +frame1047.jpg,800,800,corobot,34,205,138,409 +frame1047.jpg,800,800,corobot,140,74,239,312 +frame1047.jpg,800,800,corobot,433,114,529,341 +frame1047.jpg,800,800,corobot,170,343,263,521 +frame1047.jpg,800,800,corobot,394,502,491,654 +frame4051.jpg,800,800,corobot,144,1,232,227 +frame4051.jpg,800,800,corobot,101,364,196,541 +frame4051.jpg,800,800,corobot,377,160,462,374 +frame4051.jpg,800,800,corobot,485,288,580,451 +frame4051.jpg,800,800,corobot,406,399,485,556 +frame4051.jpg,800,800,myrobot,268,234,344,444 +frame2049.jpg,800,800,corobot,105,32,203,274 +frame2049.jpg,800,800,corobot,480,83,588,313 +frame2049.jpg,800,800,corobot,74,339,179,517 +frame2049.jpg,800,800,corobot,385,316,477,505 +frame2049.jpg,800,800,corobot,185,506,280,657 +frame2049.jpg,800,800,myrobot,269,250,345,446 +frame3030.jpg,800,800,corobot,1,360,110,532 +frame3030.jpg,800,800,corobot,378,405,467,575 +frame3030.jpg,800,800,corobot,179,147,276,370 +frame3030.jpg,800,800,corobot,414,103,505,323 +frame3030.jpg,800,800,corobot,298,7,376,250 +frame3030.jpg,800,800,myrobot,279,257,345,441 +frame2026.jpg,800,800,myrobot,264,251,345,450 +frame2026.jpg,800,800,corobot,82,41,191,298 +frame2026.jpg,800,800,corobot,86,335,189,514 +frame2026.jpg,800,800,corobot,196,508,290,662 +frame2026.jpg,800,800,corobot,404,310,503,511 +frame2026.jpg,800,800,corobot,495,87,607,315 +frame4046.jpg,800,800,corobot,150,1,240,223 +frame4046.jpg,800,800,corobot,102,363,197,533 +frame4046.jpg,800,800,corobot,372,162,451,371 +frame4046.jpg,800,800,corobot,478,277,573,444 +frame4046.jpg,800,800,myrobot,268,237,344,441 +frame4046.jpg,800,800,corobot,408,404,478,561 +frame2027.jpg,800,800,corobot,135,54,235,308 +frame2027.jpg,800,800,corobot,96,388,196,555 +frame2027.jpg,800,800,corobot,497,140,612,349 +frame2027.jpg,800,800,corobot,236,485,322,641 +frame2027.jpg,800,800,corobot,357,326,446,518 +frame2027.jpg,800,800,myrobot,267,253,343,448 +frame1027.jpg,800,800,corobot,139,70,231,315 +frame1027.jpg,800,800,corobot,23,214,131,418 +frame1027.jpg,800,800,corobot,184,351,269,520 +frame1027.jpg,800,800,corobot,436,134,531,349 +frame1027.jpg,800,800,corobot,391,495,478,654 +frame1027.jpg,800,800,myrobot,269,232,353,449 +frame4027.jpg,800,800,corobot,67,437,176,588 +frame4027.jpg,800,800,corobot,347,407,427,585 +frame4027.jpg,800,800,corobot,517,319,626,496 +frame4027.jpg,800,800,corobot,87,4,182,243 +frame4027.jpg,800,800,corobot,347,112,421,322 +frame4027.jpg,800,800,myrobot,269,235,344,446 +frame1048.jpg,800,800,corobot,38,214,141,416 +frame1048.jpg,800,800,corobot,130,82,230,315 +frame1048.jpg,800,800,corobot,162,329,251,510 +frame1048.jpg,800,800,corobot,430,121,520,341 +frame1048.jpg,800,800,corobot,387,509,480,659 +frame1048.jpg,800,800,myrobot,272,233,354,444 +frame1033.jpg,800,800,myrobot,270,233,352,449 +frame1033.jpg,800,800,corobot,33,221,139,420 +frame1033.jpg,800,800,corobot,108,88,207,328 +frame1033.jpg,800,800,corobot,413,146,503,359 +frame1033.jpg,800,800,corobot,170,326,255,502 +frame1033.jpg,800,800,corobot,367,516,453,668 +frame1041.jpg,800,800,myrobot,267,233,355,446 +frame1041.jpg,800,800,corobot,12,185,125,396 +frame1041.jpg,800,800,corobot,161,61,254,300 +frame1041.jpg,800,800,corobot,449,100,545,328 +frame1041.jpg,800,800,corobot,194,365,281,536 +frame1041.jpg,800,800,corobot,401,491,498,650 +frame2047.jpg,800,800,corobot,133,65,228,305 +frame2047.jpg,800,800,corobot,101,377,203,549 +frame2047.jpg,800,800,corobot,505,118,617,339 +frame2047.jpg,800,800,corobot,233,473,321,631 +frame2047.jpg,800,800,corobot,357,342,448,527 +frame2047.jpg,800,800,myrobot,265,255,343,450 +frame2000.jpg,800,800,corobot,79,51,179,300 +frame2000.jpg,800,800,corobot,497,87,607,308 +frame2000.jpg,800,800,corobot,85,329,193,500 +frame2000.jpg,800,800,corobot,413,318,503,514 +frame2000.jpg,800,800,corobot,185,513,275,668 +frame2000.jpg,800,800,myrobot,266,254,342,449 +frame1049.jpg,800,800,myrobot,271,233,356,448 +frame1049.jpg,800,800,corobot,1,162,117,379 +frame1049.jpg,800,800,corobot,174,53,266,295 +frame1049.jpg,800,800,corobot,464,88,562,316 +frame1049.jpg,800,800,corobot,198,373,284,545 +frame1049.jpg,800,800,corobot,425,476,523,634 +frame1039.jpg,800,800,myrobot,269,232,353,448 +frame1039.jpg,800,800,corobot,44,218,146,422 +frame1039.jpg,800,800,corobot,116,86,212,323 +frame1039.jpg,800,800,corobot,171,328,257,510 +frame1039.jpg,800,800,corobot,413,136,508,355 +frame1039.jpg,800,800,corobot,357,524,449,676 +frame2043.jpg,800,800,corobot,102,39,200,271 +frame2043.jpg,800,800,corobot,485,95,591,316 +frame2043.jpg,800,800,corobot,71,336,176,517 +frame2043.jpg,800,800,corobot,385,317,478,500 +frame2043.jpg,800,800,corobot,192,509,290,659 +frame2043.jpg,800,800,myrobot,264,255,344,446 +frame2029.jpg,800,800,corobot,97,39,197,294 +frame2029.jpg,800,800,corobot,485,86,592,311 +frame2029.jpg,800,800,corobot,78,342,182,516 +frame2029.jpg,800,800,corobot,392,309,485,508 +frame2029.jpg,800,800,corobot,194,514,281,664 +frame2029.jpg,800,800,myrobot,269,251,342,448 +frame3032.jpg,800,800,corobot,17,405,129,574 +frame3032.jpg,800,800,corobot,414,438,504,599 +frame3032.jpg,800,800,corobot,133,172,225,386 +frame3032.jpg,800,800,corobot,436,147,530,359 +frame3032.jpg,800,800,corobot,335,3,417,225 +frame3032.jpg,800,800,myrobot,274,231,345,443 +frame4000.jpg,800,800,corobot,94,1,191,246 +frame4000.jpg,800,800,corobot,351,111,425,322 +frame4000.jpg,800,800,corobot,75,446,186,597 +frame4000.jpg,800,800,corobot,339,410,416,587 +frame4000.jpg,800,800,corobot,517,334,635,516 +frame4000.jpg,800,800,myrobot,264,228,344,446 +frame4039.jpg,800,800,corobot,129,1,219,242 +frame4039.jpg,800,800,corobot,76,391,171,555 +frame4039.jpg,800,800,corobot,345,124,419,322 +frame4039.jpg,800,800,myrobot,267,242,343,445 +frame4039.jpg,800,800,corobot,374,424,458,584 +frame4039.jpg,800,800,corobot,511,319,617,496 +frame2039.jpg,800,800,corobot,152,91,243,321 +frame2039.jpg,800,800,corobot,115,400,211,563 +frame2039.jpg,800,800,corobot,247,467,331,630 +frame2039.jpg,800,800,corobot,349,348,437,531 +frame2039.jpg,800,800,corobot,524,157,641,368 +frame2039.jpg,800,800,myrobot,267,255,342,446 diff --git a/workspace/virtuallab/object_detection/data_decoders/__init__.py b/workspace/virtuallab/object_detection/data_decoders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/virtuallab/object_detection/data_decoders/__init__.pyc b/workspace/virtuallab/object_detection/data_decoders/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff136cd501b909ee13d2539b2d3bf2c9c8e7f4dd GIT binary patch literal 146 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/data_decoders/__pycache__/tf_sequence_example_decoder.cpython-36.pyc b/workspace/virtuallab/object_detection/data_decoders/__pycache__/tf_sequence_example_decoder.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c60425f4c9395292782cf77dba79205a6cb5386 GIT binary patch literal 10454 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.py b/workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.py new file mode 100644 index 0000000..b7a55e4 --- /dev/null +++ b/workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.py @@ -0,0 +1,881 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tensorflow Example proto decoder for object detection. + +A decoder to decode string tensors containing serialized tensorflow.Example +protos for object detection. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import enum +import numpy as np +from six.moves import zip +import tensorflow.compat.v1 as tf +from tf_slim import tfexample_decoder as slim_example_decoder +from object_detection.core import data_decoder +from object_detection.core import standard_fields as fields +from object_detection.protos import input_reader_pb2 +from object_detection.utils import label_map_util +from object_detection.utils import shape_utils + +# pylint: disable=g-import-not-at-top +try: + from tensorflow.contrib import lookup as contrib_lookup + +except ImportError: + # TF 2.0 doesn't ship with contrib. + pass +# pylint: enable=g-import-not-at-top + +_LABEL_OFFSET = 1 + + +class Visibility(enum.Enum): + """Visibility definitions. + + This follows the MS Coco convention (http://cocodataset.org/#format-data). + """ + # Keypoint is not labeled. + UNLABELED = 0 + # Keypoint is labeled but falls outside the object segment (e.g. occluded). + NOT_VISIBLE = 1 + # Keypoint is labeled and visible. + VISIBLE = 2 + + +class _ClassTensorHandler(slim_example_decoder.Tensor): + """An ItemHandler to fetch class ids from class text.""" + + def __init__(self, + tensor_key, + label_map_proto_file, + shape_keys=None, + shape=None, + default_value=''): + """Initializes the LookupTensor handler. + + Simply calls a vocabulary (most often, a label mapping) lookup. + + Args: + tensor_key: the name of the `TFExample` feature to read the tensor from. + label_map_proto_file: File path to a text format LabelMapProto message + mapping class text to id. + shape_keys: Optional name or list of names of the TF-Example feature in + which the tensor shape is stored. If a list, then each corresponds to + one dimension of the shape. + shape: Optional output shape of the `Tensor`. If provided, the `Tensor` is + reshaped accordingly. + default_value: The value used when the `tensor_key` is not found in a + particular `TFExample`. + + Raises: + ValueError: if both `shape_keys` and `shape` are specified. + """ + name_to_id = label_map_util.get_label_map_dict( + label_map_proto_file, use_display_name=False) + # We use a default_value of -1, but we expect all labels to be contained + # in the label map. + try: + # Dynamically try to load the tf v2 lookup, falling back to contrib + lookup = tf.compat.v2.lookup + hash_table_class = tf.compat.v2.lookup.StaticHashTable + except AttributeError: + lookup = contrib_lookup + hash_table_class = contrib_lookup.HashTable + name_to_id_table = hash_table_class( + initializer=lookup.KeyValueTensorInitializer( + keys=tf.constant(list(name_to_id.keys())), + values=tf.constant(list(name_to_id.values()), dtype=tf.int64)), + default_value=-1) + display_name_to_id = label_map_util.get_label_map_dict( + label_map_proto_file, use_display_name=True) + # We use a default_value of -1, but we expect all labels to be contained + # in the label map. + display_name_to_id_table = hash_table_class( + initializer=lookup.KeyValueTensorInitializer( + keys=tf.constant(list(display_name_to_id.keys())), + values=tf.constant( + list(display_name_to_id.values()), dtype=tf.int64)), + default_value=-1) + + self._name_to_id_table = name_to_id_table + self._display_name_to_id_table = display_name_to_id_table + super(_ClassTensorHandler, self).__init__(tensor_key, shape_keys, shape, + default_value) + + def tensors_to_item(self, keys_to_tensors): + unmapped_tensor = super(_ClassTensorHandler, + self).tensors_to_item(keys_to_tensors) + return tf.maximum(self._name_to_id_table.lookup(unmapped_tensor), + self._display_name_to_id_table.lookup(unmapped_tensor)) + + +class TfExampleDecoder(data_decoder.DataDecoder): + """Tensorflow Example proto decoder.""" + + def __init__(self, + load_instance_masks=False, + instance_mask_type=input_reader_pb2.NUMERICAL_MASKS, + label_map_proto_file=None, + use_display_name=False, + dct_method='', + num_keypoints=0, + num_additional_channels=0, + load_multiclass_scores=False, + load_context_features=False, + expand_hierarchy_labels=False, + load_dense_pose=False, + load_track_id=False): + """Constructor sets keys_to_features and items_to_handlers. + + Args: + load_instance_masks: whether or not to load and handle instance masks. + instance_mask_type: type of instance masks. Options are provided in + input_reader.proto. This is only used if `load_instance_masks` is True. + label_map_proto_file: a file path to a + object_detection.protos.StringIntLabelMap proto. If provided, then the + mapped IDs of 'image/object/class/text' will take precedence over the + existing 'image/object/class/label' ID. Also, if provided, it is + assumed that 'image/object/class/text' will be in the data. + use_display_name: whether or not to use the `display_name` for label + mapping (instead of `name`). Only used if label_map_proto_file is + provided. + dct_method: An optional string. Defaults to None. It only takes + effect when image format is jpeg, used to specify a hint about the + algorithm used for jpeg decompression. Currently valid values + are ['INTEGER_FAST', 'INTEGER_ACCURATE']. The hint may be ignored, for + example, the jpeg library does not have that specific option. + num_keypoints: the number of keypoints per object. + num_additional_channels: how many additional channels to use. + load_multiclass_scores: Whether to load multiclass scores associated with + boxes. + load_context_features: Whether to load information from context_features, + to provide additional context to a detection model for training and/or + inference. + expand_hierarchy_labels: Expands the object and image labels taking into + account the provided hierarchy in the label_map_proto_file. For positive + classes, the labels are extended to ancestor. For negative classes, + the labels are expanded to descendants. + load_dense_pose: Whether to load DensePose annotations. + load_track_id: Whether to load tracking annotations. + + Raises: + ValueError: If `instance_mask_type` option is not one of + input_reader_pb2.DEFAULT, input_reader_pb2.NUMERICAL, or + input_reader_pb2.PNG_MASKS. + ValueError: If `expand_labels_hierarchy` is True, but the + `label_map_proto_file` is not provided. + """ + # TODO(rathodv): delete unused `use_display_name` argument once we change + # other decoders to handle label maps similarly. + del use_display_name + self.keys_to_features = { + 'image/encoded': + tf.FixedLenFeature((), tf.string, default_value=''), + 'image/format': + tf.FixedLenFeature((), tf.string, default_value='jpeg'), + 'image/filename': + tf.FixedLenFeature((), tf.string, default_value=''), + 'image/key/sha256': + tf.FixedLenFeature((), tf.string, default_value=''), + 'image/source_id': + tf.FixedLenFeature((), tf.string, default_value=''), + 'image/height': + tf.FixedLenFeature((), tf.int64, default_value=1), + 'image/width': + tf.FixedLenFeature((), tf.int64, default_value=1), + # Image-level labels. + 'image/class/text': + tf.VarLenFeature(tf.string), + 'image/class/label': + tf.VarLenFeature(tf.int64), + 'image/neg_category_ids': + tf.VarLenFeature(tf.int64), + 'image/not_exhaustive_category_ids': + tf.VarLenFeature(tf.int64), + 'image/class/confidence': + tf.VarLenFeature(tf.float32), + # Object boxes and classes. + 'image/object/bbox/xmin': + tf.VarLenFeature(tf.float32), + 'image/object/bbox/xmax': + tf.VarLenFeature(tf.float32), + 'image/object/bbox/ymin': + tf.VarLenFeature(tf.float32), + 'image/object/bbox/ymax': + tf.VarLenFeature(tf.float32), + 'image/object/class/label': + tf.VarLenFeature(tf.int64), + 'image/object/class/text': + tf.VarLenFeature(tf.string), + 'image/object/area': + tf.VarLenFeature(tf.float32), + 'image/object/is_crowd': + tf.VarLenFeature(tf.int64), + 'image/object/difficult': + tf.VarLenFeature(tf.int64), + 'image/object/group_of': + tf.VarLenFeature(tf.int64), + 'image/object/weight': + tf.VarLenFeature(tf.float32), + + } + # We are checking `dct_method` instead of passing it directly in order to + # ensure TF version 1.6 compatibility. + if dct_method: + image = slim_example_decoder.Image( + image_key='image/encoded', + format_key='image/format', + channels=3, + dct_method=dct_method) + additional_channel_image = slim_example_decoder.Image( + image_key='image/additional_channels/encoded', + format_key='image/format', + channels=1, + repeated=True, + dct_method=dct_method) + else: + image = slim_example_decoder.Image( + image_key='image/encoded', format_key='image/format', channels=3) + additional_channel_image = slim_example_decoder.Image( + image_key='image/additional_channels/encoded', + format_key='image/format', + channels=1, + repeated=True) + self.items_to_handlers = { + fields.InputDataFields.image: + image, + fields.InputDataFields.source_id: ( + slim_example_decoder.Tensor('image/source_id')), + fields.InputDataFields.key: ( + slim_example_decoder.Tensor('image/key/sha256')), + fields.InputDataFields.filename: ( + slim_example_decoder.Tensor('image/filename')), + # Image-level labels. + fields.InputDataFields.groundtruth_image_confidences: ( + slim_example_decoder.Tensor('image/class/confidence')), + fields.InputDataFields.groundtruth_verified_neg_classes: ( + slim_example_decoder.Tensor('image/neg_category_ids')), + fields.InputDataFields.groundtruth_not_exhaustive_classes: ( + slim_example_decoder.Tensor('image/not_exhaustive_category_ids')), + # Object boxes and classes. + fields.InputDataFields.groundtruth_boxes: ( + slim_example_decoder.BoundingBox(['ymin', 'xmin', 'ymax', 'xmax'], + 'image/object/bbox/')), + fields.InputDataFields.groundtruth_area: + slim_example_decoder.Tensor('image/object/area'), + fields.InputDataFields.groundtruth_is_crowd: ( + slim_example_decoder.Tensor('image/object/is_crowd')), + fields.InputDataFields.groundtruth_difficult: ( + slim_example_decoder.Tensor('image/object/difficult')), + fields.InputDataFields.groundtruth_group_of: ( + slim_example_decoder.Tensor('image/object/group_of')), + fields.InputDataFields.groundtruth_weights: ( + slim_example_decoder.Tensor('image/object/weight')), + + } + if load_multiclass_scores: + self.keys_to_features[ + 'image/object/class/multiclass_scores'] = tf.VarLenFeature(tf.float32) + self.items_to_handlers[fields.InputDataFields.multiclass_scores] = ( + slim_example_decoder.Tensor('image/object/class/multiclass_scores')) + + if load_context_features: + self.keys_to_features[ + 'image/context_features'] = tf.VarLenFeature(tf.float32) + self.items_to_handlers[fields.InputDataFields.context_features] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/context_features', 'image/context_feature_length'], + self._reshape_context_features)) + + self.keys_to_features[ + 'image/context_feature_length'] = tf.FixedLenFeature((), tf.int64) + self.items_to_handlers[fields.InputDataFields.context_feature_length] = ( + slim_example_decoder.Tensor('image/context_feature_length')) + + if num_additional_channels > 0: + self.keys_to_features[ + 'image/additional_channels/encoded'] = tf.FixedLenFeature( + (num_additional_channels,), tf.string) + self.items_to_handlers[ + fields.InputDataFields. + image_additional_channels] = additional_channel_image + self._num_keypoints = num_keypoints + if num_keypoints > 0: + self.keys_to_features['image/object/keypoint/x'] = ( + tf.VarLenFeature(tf.float32)) + self.keys_to_features['image/object/keypoint/y'] = ( + tf.VarLenFeature(tf.float32)) + self.keys_to_features['image/object/keypoint/visibility'] = ( + tf.VarLenFeature(tf.int64)) + self.items_to_handlers[fields.InputDataFields.groundtruth_keypoints] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/object/keypoint/y', 'image/object/keypoint/x'], + self._reshape_keypoints)) + kpt_vis_field = fields.InputDataFields.groundtruth_keypoint_visibilities + self.items_to_handlers[kpt_vis_field] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/object/keypoint/x', 'image/object/keypoint/visibility'], + self._reshape_keypoint_visibilities)) + if load_instance_masks: + if instance_mask_type in (input_reader_pb2.DEFAULT, + input_reader_pb2.NUMERICAL_MASKS): + self.keys_to_features['image/object/mask'] = ( + tf.VarLenFeature(tf.float32)) + self.items_to_handlers[ + fields.InputDataFields.groundtruth_instance_masks] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/object/mask', 'image/height', 'image/width'], + self._reshape_instance_masks)) + elif instance_mask_type == input_reader_pb2.PNG_MASKS: + self.keys_to_features['image/object/mask'] = tf.VarLenFeature(tf.string) + self.items_to_handlers[ + fields.InputDataFields.groundtruth_instance_masks] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/object/mask', 'image/height', 'image/width'], + self._decode_png_instance_masks)) + else: + raise ValueError('Did not recognize the `instance_mask_type` option.') + if load_dense_pose: + self.keys_to_features['image/object/densepose/num'] = ( + tf.VarLenFeature(tf.int64)) + self.keys_to_features['image/object/densepose/part_index'] = ( + tf.VarLenFeature(tf.int64)) + self.keys_to_features['image/object/densepose/x'] = ( + tf.VarLenFeature(tf.float32)) + self.keys_to_features['image/object/densepose/y'] = ( + tf.VarLenFeature(tf.float32)) + self.keys_to_features['image/object/densepose/u'] = ( + tf.VarLenFeature(tf.float32)) + self.keys_to_features['image/object/densepose/v'] = ( + tf.VarLenFeature(tf.float32)) + self.items_to_handlers[ + fields.InputDataFields.groundtruth_dp_num_points] = ( + slim_example_decoder.Tensor('image/object/densepose/num')) + self.items_to_handlers[fields.InputDataFields.groundtruth_dp_part_ids] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/object/densepose/part_index', + 'image/object/densepose/num'], self._dense_pose_part_indices)) + self.items_to_handlers[ + fields.InputDataFields.groundtruth_dp_surface_coords] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/object/densepose/x', 'image/object/densepose/y', + 'image/object/densepose/u', 'image/object/densepose/v', + 'image/object/densepose/num'], + self._dense_pose_surface_coordinates)) + if load_track_id: + self.keys_to_features['image/object/track/label'] = ( + tf.VarLenFeature(tf.int64)) + self.items_to_handlers[ + fields.InputDataFields.groundtruth_track_ids] = ( + slim_example_decoder.Tensor('image/object/track/label')) + + if label_map_proto_file: + # If the label_map_proto is provided, try to use it in conjunction with + # the class text, and fall back to a materialized ID. + label_handler = slim_example_decoder.BackupHandler( + _ClassTensorHandler( + 'image/object/class/text', label_map_proto_file, + default_value=''), + slim_example_decoder.Tensor('image/object/class/label')) + image_label_handler = slim_example_decoder.BackupHandler( + _ClassTensorHandler( + fields.TfExampleFields.image_class_text, + label_map_proto_file, + default_value=''), + slim_example_decoder.Tensor(fields.TfExampleFields.image_class_label)) + else: + label_handler = slim_example_decoder.Tensor('image/object/class/label') + image_label_handler = slim_example_decoder.Tensor( + fields.TfExampleFields.image_class_label) + self.items_to_handlers[ + fields.InputDataFields.groundtruth_classes] = label_handler + self.items_to_handlers[ + fields.InputDataFields.groundtruth_image_classes] = image_label_handler + + self._expand_hierarchy_labels = expand_hierarchy_labels + self._ancestors_lut = None + self._descendants_lut = None + if expand_hierarchy_labels: + if label_map_proto_file: + ancestors_lut, descendants_lut = ( + label_map_util.get_label_map_hierarchy_lut(label_map_proto_file, + True)) + self._ancestors_lut = tf.constant(ancestors_lut, dtype=tf.int64) + self._descendants_lut = tf.constant(descendants_lut, dtype=tf.int64) + else: + raise ValueError('In order to expand labels, the label_map_proto_file ' + 'has to be provided.') + + def decode(self, tf_example_string_tensor): + """Decodes serialized tensorflow example and returns a tensor dictionary. + + Args: + tf_example_string_tensor: a string tensor holding a serialized tensorflow + example proto. + + Returns: + A dictionary of the following tensors. + fields.InputDataFields.image - 3D uint8 tensor of shape [None, None, 3] + containing image. + fields.InputDataFields.original_image_spatial_shape - 1D int32 tensor of + shape [2] containing shape of the image. + fields.InputDataFields.source_id - string tensor containing original + image id. + fields.InputDataFields.key - string tensor with unique sha256 hash key. + fields.InputDataFields.filename - string tensor with original dataset + filename. + fields.InputDataFields.groundtruth_boxes - 2D float32 tensor of shape + [None, 4] containing box corners. + fields.InputDataFields.groundtruth_classes - 1D int64 tensor of shape + [None] containing classes for the boxes. + fields.InputDataFields.groundtruth_weights - 1D float32 tensor of + shape [None] indicating the weights of groundtruth boxes. + fields.InputDataFields.groundtruth_area - 1D float32 tensor of shape + [None] containing containing object mask area in pixel squared. + fields.InputDataFields.groundtruth_is_crowd - 1D bool tensor of shape + [None] indicating if the boxes enclose a crowd. + + Optional: + fields.InputDataFields.groundtruth_image_confidences - 1D float tensor of + shape [None] indicating if a class is present in the image (1.0) or + a class is not present in the image (0.0). + fields.InputDataFields.image_additional_channels - 3D uint8 tensor of + shape [None, None, num_additional_channels]. 1st dim is height; 2nd dim + is width; 3rd dim is the number of additional channels. + fields.InputDataFields.groundtruth_difficult - 1D bool tensor of shape + [None] indicating if the boxes represent `difficult` instances. + fields.InputDataFields.groundtruth_group_of - 1D bool tensor of shape + [None] indicating if the boxes represent `group_of` instances. + fields.InputDataFields.groundtruth_keypoints - 3D float32 tensor of + shape [None, num_keypoints, 2] containing keypoints, where the + coordinates of the keypoints are ordered (y, x). + fields.InputDataFields.groundtruth_keypoint_visibilities - 2D bool + tensor of shape [None, num_keypoints] containing keypoint visibilites. + fields.InputDataFields.groundtruth_instance_masks - 3D float32 tensor of + shape [None, None, None] containing instance masks. + fields.InputDataFields.groundtruth_image_classes - 1D int64 of shape + [None] containing classes for the boxes. + fields.InputDataFields.multiclass_scores - 1D float32 tensor of shape + [None * num_classes] containing flattened multiclass scores for + groundtruth boxes. + fields.InputDataFields.context_features - 1D float32 tensor of shape + [context_feature_length * num_context_features] + fields.InputDataFields.context_feature_length - int32 tensor specifying + the length of each feature in context_features + """ + serialized_example = tf.reshape(tf_example_string_tensor, shape=[]) + decoder = slim_example_decoder.TFExampleDecoder(self.keys_to_features, + self.items_to_handlers) + keys = decoder.list_items() + tensors = decoder.decode(serialized_example, items=keys) + tensor_dict = dict(zip(keys, tensors)) + is_crowd = fields.InputDataFields.groundtruth_is_crowd + tensor_dict[is_crowd] = tf.cast(tensor_dict[is_crowd], dtype=tf.bool) + tensor_dict[fields.InputDataFields.image].set_shape([None, None, 3]) + tensor_dict[fields.InputDataFields.original_image_spatial_shape] = tf.shape( + tensor_dict[fields.InputDataFields.image])[:2] + + if fields.InputDataFields.image_additional_channels in tensor_dict: + channels = tensor_dict[fields.InputDataFields.image_additional_channels] + channels = tf.squeeze(channels, axis=3) + channels = tf.transpose(channels, perm=[1, 2, 0]) + tensor_dict[fields.InputDataFields.image_additional_channels] = channels + + def default_groundtruth_weights(): + return tf.ones( + [tf.shape(tensor_dict[fields.InputDataFields.groundtruth_boxes])[0]], + dtype=tf.float32) + + tensor_dict[fields.InputDataFields.groundtruth_weights] = tf.cond( + tf.greater( + tf.shape( + tensor_dict[fields.InputDataFields.groundtruth_weights])[0], + 0), lambda: tensor_dict[fields.InputDataFields.groundtruth_weights], + default_groundtruth_weights) + + if fields.InputDataFields.groundtruth_keypoints in tensor_dict: + # Set all keypoints that are not labeled to NaN. + gt_kpt_fld = fields.InputDataFields.groundtruth_keypoints + gt_kpt_vis_fld = fields.InputDataFields.groundtruth_keypoint_visibilities + visibilities_tiled = tf.tile( + tf.expand_dims(tensor_dict[gt_kpt_vis_fld], -1), + [1, 1, 2]) + tensor_dict[gt_kpt_fld] = tf.where( + visibilities_tiled, + tensor_dict[gt_kpt_fld], + np.nan * tf.ones_like(tensor_dict[gt_kpt_fld])) + + if self._expand_hierarchy_labels: + input_fields = fields.InputDataFields + image_classes, image_confidences = self._expand_image_label_hierarchy( + tensor_dict[input_fields.groundtruth_image_classes], + tensor_dict[input_fields.groundtruth_image_confidences]) + tensor_dict[input_fields.groundtruth_image_classes] = image_classes + tensor_dict[input_fields.groundtruth_image_confidences] = ( + image_confidences) + + box_fields = [ + fields.InputDataFields.groundtruth_group_of, + fields.InputDataFields.groundtruth_is_crowd, + fields.InputDataFields.groundtruth_difficult, + fields.InputDataFields.groundtruth_area, + fields.InputDataFields.groundtruth_boxes, + fields.InputDataFields.groundtruth_weights, + ] + + def expand_field(field_name): + return self._expansion_box_field_labels( + tensor_dict[input_fields.groundtruth_classes], + tensor_dict[field_name]) + + # pylint: disable=cell-var-from-loop + for field in box_fields: + if field in tensor_dict: + tensor_dict[field] = tf.cond( + tf.size(tensor_dict[field]) > 0, lambda: expand_field(field), + lambda: tensor_dict[field]) + # pylint: enable=cell-var-from-loop + + tensor_dict[input_fields.groundtruth_classes] = ( + self._expansion_box_field_labels( + tensor_dict[input_fields.groundtruth_classes], + tensor_dict[input_fields.groundtruth_classes], True)) + + if fields.InputDataFields.groundtruth_group_of in tensor_dict: + group_of = fields.InputDataFields.groundtruth_group_of + tensor_dict[group_of] = tf.cast(tensor_dict[group_of], dtype=tf.bool) + + if fields.InputDataFields.groundtruth_dp_num_points in tensor_dict: + tensor_dict[fields.InputDataFields.groundtruth_dp_num_points] = tf.cast( + tensor_dict[fields.InputDataFields.groundtruth_dp_num_points], + dtype=tf.int32) + tensor_dict[fields.InputDataFields.groundtruth_dp_part_ids] = tf.cast( + tensor_dict[fields.InputDataFields.groundtruth_dp_part_ids], + dtype=tf.int32) + + if fields.InputDataFields.groundtruth_track_ids in tensor_dict: + tensor_dict[fields.InputDataFields.groundtruth_track_ids] = tf.cast( + tensor_dict[fields.InputDataFields.groundtruth_track_ids], + dtype=tf.int32) + + return tensor_dict + + def _reshape_keypoints(self, keys_to_tensors): + """Reshape keypoints. + + The keypoints are reshaped to [num_instances, num_keypoints, 2]. + + Args: + keys_to_tensors: a dictionary from keys to tensors. Expected keys are: + 'image/object/keypoint/x' + 'image/object/keypoint/y' + + Returns: + A 3-D float tensor of shape [num_instances, num_keypoints, 2] with values + in [0, 1]. + """ + y = keys_to_tensors['image/object/keypoint/y'] + if isinstance(y, tf.SparseTensor): + y = tf.sparse_tensor_to_dense(y) + y = tf.expand_dims(y, 1) + x = keys_to_tensors['image/object/keypoint/x'] + if isinstance(x, tf.SparseTensor): + x = tf.sparse_tensor_to_dense(x) + x = tf.expand_dims(x, 1) + keypoints = tf.concat([y, x], 1) + keypoints = tf.reshape(keypoints, [-1, self._num_keypoints, 2]) + return keypoints + + def _reshape_keypoint_visibilities(self, keys_to_tensors): + """Reshape keypoint visibilities. + + The keypoint visibilities are reshaped to [num_instances, + num_keypoints]. + + The raw keypoint visibilities are expected to conform to the + MSCoco definition. See Visibility enum. + + The returned boolean is True for the labeled case (either + Visibility.NOT_VISIBLE or Visibility.VISIBLE). These are the same categories + that COCO uses to evaluate keypoint detection performance: + http://cocodataset.org/#keypoints-eval + + If image/object/keypoint/visibility is not provided, visibilities will be + set to True for finite keypoint coordinate values, and 0 if the coordinates + are NaN. + + Args: + keys_to_tensors: a dictionary from keys to tensors. Expected keys are: + 'image/object/keypoint/x' + 'image/object/keypoint/visibility' + + Returns: + A 2-D bool tensor of shape [num_instances, num_keypoints] with values + in {0, 1}. 1 if the keypoint is labeled, 0 otherwise. + """ + x = keys_to_tensors['image/object/keypoint/x'] + vis = keys_to_tensors['image/object/keypoint/visibility'] + if isinstance(vis, tf.SparseTensor): + vis = tf.sparse_tensor_to_dense(vis) + if isinstance(x, tf.SparseTensor): + x = tf.sparse_tensor_to_dense(x) + + default_vis = tf.where( + tf.math.is_nan(x), + Visibility.UNLABELED.value * tf.ones_like(x, dtype=tf.int64), + Visibility.VISIBLE.value * tf.ones_like(x, dtype=tf.int64)) + # Use visibility if provided, otherwise use the default visibility. + vis = tf.cond(tf.equal(tf.size(x), tf.size(vis)), + true_fn=lambda: vis, + false_fn=lambda: default_vis) + vis = tf.math.logical_or( + tf.math.equal(vis, Visibility.NOT_VISIBLE.value), + tf.math.equal(vis, Visibility.VISIBLE.value)) + vis = tf.reshape(vis, [-1, self._num_keypoints]) + return vis + + def _reshape_instance_masks(self, keys_to_tensors): + """Reshape instance segmentation masks. + + The instance segmentation masks are reshaped to [num_instances, height, + width]. + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 3-D float tensor of shape [num_instances, height, width] with values + in {0, 1}. + """ + height = keys_to_tensors['image/height'] + width = keys_to_tensors['image/width'] + to_shape = tf.cast(tf.stack([-1, height, width]), tf.int32) + masks = keys_to_tensors['image/object/mask'] + if isinstance(masks, tf.SparseTensor): + masks = tf.sparse_tensor_to_dense(masks) + masks = tf.reshape( + tf.cast(tf.greater(masks, 0.0), dtype=tf.float32), to_shape) + return tf.cast(masks, tf.float32) + + def _reshape_context_features(self, keys_to_tensors): + """Reshape context features. + + The instance context_features are reshaped to + [num_context_features, context_feature_length] + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 2-D float tensor of shape [num_context_features, context_feature_length] + """ + context_feature_length = keys_to_tensors['image/context_feature_length'] + to_shape = tf.cast(tf.stack([-1, context_feature_length]), tf.int32) + context_features = keys_to_tensors['image/context_features'] + if isinstance(context_features, tf.SparseTensor): + context_features = tf.sparse_tensor_to_dense(context_features) + context_features = tf.reshape(context_features, to_shape) + return context_features + + def _decode_png_instance_masks(self, keys_to_tensors): + """Decode PNG instance segmentation masks and stack into dense tensor. + + The instance segmentation masks are reshaped to [num_instances, height, + width]. + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 3-D float tensor of shape [num_instances, height, width] with values + in {0, 1}. + """ + + def decode_png_mask(image_buffer): + image = tf.squeeze( + tf.image.decode_image(image_buffer, channels=1), axis=2) + image.set_shape([None, None]) + image = tf.cast(tf.greater(image, 0), dtype=tf.float32) + return image + + png_masks = keys_to_tensors['image/object/mask'] + height = keys_to_tensors['image/height'] + width = keys_to_tensors['image/width'] + if isinstance(png_masks, tf.SparseTensor): + png_masks = tf.sparse_tensor_to_dense(png_masks, default_value='') + return tf.cond( + tf.greater(tf.size(png_masks), 0), + lambda: tf.map_fn(decode_png_mask, png_masks, dtype=tf.float32), + lambda: tf.zeros(tf.cast(tf.stack([0, height, width]), dtype=tf.int32))) + + def _dense_pose_part_indices(self, keys_to_tensors): + """Creates a tensor that contains part indices for each DensePose point. + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 2-D int32 tensor of shape [num_instances, num_points] where each element + contains the DensePose part index (0-23). The value `num_points` + corresponds to the maximum number of sampled points across all instances + in the image. Note that instances with less sampled points will be padded + with zeros in the last dimension. + """ + num_points_per_instances = keys_to_tensors['image/object/densepose/num'] + part_index = keys_to_tensors['image/object/densepose/part_index'] + if isinstance(num_points_per_instances, tf.SparseTensor): + num_points_per_instances = tf.sparse_tensor_to_dense( + num_points_per_instances) + if isinstance(part_index, tf.SparseTensor): + part_index = tf.sparse_tensor_to_dense(part_index) + part_index = tf.cast(part_index, dtype=tf.int32) + max_points_per_instance = tf.cast( + tf.math.reduce_max(num_points_per_instances), dtype=tf.int32) + num_points_cumulative = tf.concat([ + [0], tf.math.cumsum(num_points_per_instances)], axis=0) + + def pad_parts_tensor(instance_ind): + points_range_start = num_points_cumulative[instance_ind] + points_range_end = num_points_cumulative[instance_ind + 1] + part_inds = part_index[points_range_start:points_range_end] + return shape_utils.pad_or_clip_nd(part_inds, + output_shape=[max_points_per_instance]) + + return tf.map_fn(pad_parts_tensor, + tf.range(tf.size(num_points_per_instances)), + dtype=tf.int32) + + def _dense_pose_surface_coordinates(self, keys_to_tensors): + """Creates a tensor that contains surface coords for each DensePose point. + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 3-D float32 tensor of shape [num_instances, num_points, 4] where each + point contains (y, x, v, u) data for each sampled DensePose point. The + (y, x) coordinate has normalized image locations for the point, and (v, u) + contains the surface coordinate (also normalized) for the part. The value + `num_points` corresponds to the maximum number of sampled points across + all instances in the image. Note that instances with less sampled points + will be padded with zeros in dim=1. + """ + num_points_per_instances = keys_to_tensors['image/object/densepose/num'] + dp_y = keys_to_tensors['image/object/densepose/y'] + dp_x = keys_to_tensors['image/object/densepose/x'] + dp_v = keys_to_tensors['image/object/densepose/v'] + dp_u = keys_to_tensors['image/object/densepose/u'] + if isinstance(num_points_per_instances, tf.SparseTensor): + num_points_per_instances = tf.sparse_tensor_to_dense( + num_points_per_instances) + if isinstance(dp_y, tf.SparseTensor): + dp_y = tf.sparse_tensor_to_dense(dp_y) + if isinstance(dp_x, tf.SparseTensor): + dp_x = tf.sparse_tensor_to_dense(dp_x) + if isinstance(dp_v, tf.SparseTensor): + dp_v = tf.sparse_tensor_to_dense(dp_v) + if isinstance(dp_u, tf.SparseTensor): + dp_u = tf.sparse_tensor_to_dense(dp_u) + max_points_per_instance = tf.cast( + tf.math.reduce_max(num_points_per_instances), dtype=tf.int32) + num_points_cumulative = tf.concat([ + [0], tf.math.cumsum(num_points_per_instances)], axis=0) + + def pad_surface_coordinates_tensor(instance_ind): + """Pads DensePose surface coordinates for each instance.""" + points_range_start = num_points_cumulative[instance_ind] + points_range_end = num_points_cumulative[instance_ind + 1] + y = dp_y[points_range_start:points_range_end] + x = dp_x[points_range_start:points_range_end] + v = dp_v[points_range_start:points_range_end] + u = dp_u[points_range_start:points_range_end] + # Create [num_points_i, 4] tensor, where num_points_i is the number of + # sampled points for instance i. + unpadded_tensor = tf.stack([y, x, v, u], axis=1) + return shape_utils.pad_or_clip_nd( + unpadded_tensor, output_shape=[max_points_per_instance, 4]) + + return tf.map_fn(pad_surface_coordinates_tensor, + tf.range(tf.size(num_points_per_instances)), + dtype=tf.float32) + + def _expand_image_label_hierarchy(self, image_classes, image_confidences): + """Expand image level labels according to the hierarchy. + + Args: + image_classes: Int64 tensor with the image level class ids for a sample. + image_confidences: Float tensor signaling whether a class id is present in + the image (1.0) or not present (0.0). + + Returns: + new_image_classes: Int64 tensor equal to expanding image_classes. + new_image_confidences: Float tensor equal to expanding image_confidences. + """ + + def expand_labels(relation_tensor, confidence_value): + """Expand to ancestors or descendants depending on arguments.""" + mask = tf.equal(image_confidences, confidence_value) + target_image_classes = tf.boolean_mask(image_classes, mask) + expanded_indices = tf.reduce_any((tf.gather( + relation_tensor, target_image_classes - _LABEL_OFFSET, axis=0) > 0), + axis=0) + expanded_indices = tf.where(expanded_indices)[:, 0] + _LABEL_OFFSET + new_groundtruth_image_classes = ( + tf.concat([ + tf.boolean_mask(image_classes, tf.logical_not(mask)), + expanded_indices, + ], + axis=0)) + new_groundtruth_image_confidences = ( + tf.concat([ + tf.boolean_mask(image_confidences, tf.logical_not(mask)), + tf.ones([tf.shape(expanded_indices)[0]], + dtype=image_confidences.dtype) * confidence_value, + ], + axis=0)) + return new_groundtruth_image_classes, new_groundtruth_image_confidences + + image_classes, image_confidences = expand_labels(self._ancestors_lut, 1.0) + new_image_classes, new_image_confidences = expand_labels( + self._descendants_lut, 0.0) + return new_image_classes, new_image_confidences + + def _expansion_box_field_labels(self, + object_classes, + object_field, + copy_class_id=False): + """Expand the labels of a specific object field according to the hierarchy. + + Args: + object_classes: Int64 tensor with the class id for each element in + object_field. + object_field: Tensor to be expanded. + copy_class_id: Boolean to choose whether to use class id values in the + output tensor instead of replicating the original values. + + Returns: + A tensor with the result of expanding object_field. + """ + expanded_indices = tf.gather( + self._ancestors_lut, object_classes - _LABEL_OFFSET, axis=0) + if copy_class_id: + new_object_field = tf.where(expanded_indices > 0)[:, 1] + _LABEL_OFFSET + else: + new_object_field = tf.repeat( + object_field, tf.reduce_sum(expanded_indices, axis=1), axis=0) + return new_object_field diff --git a/workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.pyc b/workspace/virtuallab/object_detection/data_decoders/tf_example_decoder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dca6324a61f413bf5655864e8a7541c18ade21a GIT binary patch literal 31337 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 0).reshape((2, 3))) + self.assertAllEqual( + expected_visibility, + tensor_dict[fields.InputDataFields.groundtruth_keypoint_visibilities]) + + def testDecodeKeypointNoVisibilities(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + keypoint_ys = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] + keypoint_xs = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + 'image/object/keypoint/y': + dataset_util.float_list_feature(keypoint_ys), + 'image/object/keypoint/x': + dataset_util.float_list_feature(keypoint_xs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder(num_keypoints=3) + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_boxes].get_shape().as_list()), + [None, 4]) + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_keypoints].get_shape().as_list()), + [2, 3, 2]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + expected_boxes = np.vstack([bbox_ymins, bbox_xmins, bbox_ymaxs, + bbox_xmaxs]).transpose() + self.assertAllEqual(expected_boxes, + tensor_dict[fields.InputDataFields.groundtruth_boxes]) + + expected_keypoints = ( + np.vstack([keypoint_ys, keypoint_xs]).transpose().reshape((2, 3, 2))) + self.assertAllEqual( + expected_keypoints, + tensor_dict[fields.InputDataFields.groundtruth_keypoints]) + + expected_visibility = np.ones((2, 3)) + self.assertAllEqual( + expected_visibility, + tensor_dict[fields.InputDataFields.groundtruth_keypoint_visibilities]) + + def testDecodeDefaultGroundtruthWeights(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_boxes].get_shape().as_list()), + [None, 4]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllClose(tensor_dict[fields.InputDataFields.groundtruth_weights], + np.ones(2, dtype=np.float32)) + + def testDecodeObjectLabel(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes = [0, 1] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/label': + dataset_util.int64_list_feature(bbox_classes), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [2]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + + self.assertAllEqual(bbox_classes, + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeMultiClassScores(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + flattened_multiclass_scores = [100., 50.] + [20., 30.] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/multiclass_scores': + dataset_util.float_list_feature( + flattened_multiclass_scores), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder( + load_multiclass_scores=True) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual(flattened_multiclass_scores, + tensor_dict[fields.InputDataFields.multiclass_scores]) + + def testDecodeEmptyMultiClassScores(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder( + load_multiclass_scores=True) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertEqual( + (0,), tensor_dict[fields.InputDataFields.multiclass_scores].shape) + + def testDecodeObjectLabelNoText(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes = [1, 2] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/label': + dataset_util.int64_list_feature(bbox_classes), + })).SerializeToString() + label_map_string = """ + item { + id:1 + name:'cat' + } + item { + id:2 + name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [None]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + + self.assertAllEqual(bbox_classes, + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectLabelWithText(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes_text = [six.b('cat'), six.b('dog')] + # Annotation label gets overridden by labelmap id. + annotated_bbox_classes = [3, 4] + expected_bbox_classes = [1, 2] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/text': + dataset_util.bytes_list_feature(bbox_classes_text), + 'image/object/class/label': + dataset_util.int64_list_feature(annotated_bbox_classes), + })).SerializeToString() + label_map_string = """ + item { + id:1 + name:'cat' + } + item { + id:2 + name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + + self.assertAllEqual(expected_bbox_classes, + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectLabelUnrecognizedName(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes_text = [six.b('cat'), six.b('cheetah')] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/text': + dataset_util.bytes_list_feature(bbox_classes_text), + })).SerializeToString() + + label_map_string = """ + item { + id:2 + name:'cat' + } + item { + id:1 + name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + output = example_decoder.decode(tf.convert_to_tensor(example)) + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [None]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual([2, -1], + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectLabelWithMappingWithDisplayName(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes_text = [six.b('cat'), six.b('dog')] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/text': + dataset_util.bytes_list_feature(bbox_classes_text), + })).SerializeToString() + + label_map_string = """ + item { + id:3 + display_name:'cat' + } + item { + id:1 + display_name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [None]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual([3, 1], + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectLabelUnrecognizedNameWithMappingWithDisplayName(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes_text = [six.b('cat'), six.b('cheetah')] + bbox_classes_id = [5, 6] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/text': + dataset_util.bytes_list_feature(bbox_classes_text), + 'image/object/class/label': + dataset_util.int64_list_feature(bbox_classes_id), + })).SerializeToString() + + label_map_string = """ + item { + name:'/m/cat' + id:3 + display_name:'cat' + } + item { + name:'/m/dog' + id:1 + display_name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual([3, -1], + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectLabelWithMappingWithName(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_classes_text = [six.b('cat'), six.b('dog')] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/class/text': + dataset_util.bytes_list_feature(bbox_classes_text), + })).SerializeToString() + + label_map_string = """ + item { + id:3 + name:'cat' + } + item { + id:1 + name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [None]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual([3, 1], + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testDecodeObjectArea(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + object_area = [100., 174.] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/area': + dataset_util.float_list_feature(object_area), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_area].get_shape().as_list()), [2]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + + self.assertAllEqual(object_area, + tensor_dict[fields.InputDataFields.groundtruth_area]) + + def testDecodeVerifiedNegClasses(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + neg_category_ids = [0, 5, 8] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/neg_category_ids': + dataset_util.int64_list_feature(neg_category_ids), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + neg_category_ids, + tensor_dict[fields.InputDataFields.groundtruth_verified_neg_classes]) + + def testDecodeNotExhaustiveClasses(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + not_exhaustive_category_ids = [0, 5, 8] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/not_exhaustive_category_ids': + dataset_util.int64_list_feature( + not_exhaustive_category_ids), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + not_exhaustive_category_ids, + tensor_dict[fields.InputDataFields.groundtruth_not_exhaustive_classes]) + + def testDecodeObjectIsCrowd(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + object_is_crowd = [0, 1] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/is_crowd': + dataset_util.int64_list_feature(object_is_crowd), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_is_crowd].get_shape().as_list()), + [2]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + [bool(item) for item in object_is_crowd], + tensor_dict[fields.InputDataFields.groundtruth_is_crowd]) + + def testDecodeObjectDifficult(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + object_difficult = [0, 1] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/difficult': + dataset_util.int64_list_feature(object_difficult), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_difficult].get_shape().as_list()), + [2]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + [bool(item) for item in object_difficult], + tensor_dict[fields.InputDataFields.groundtruth_difficult]) + + def testDecodeObjectGroupOf(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + object_group_of = [0, 1] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/group_of': + dataset_util.int64_list_feature(object_group_of), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_group_of].get_shape().as_list()), + [2]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + [bool(item) for item in object_group_of], + tensor_dict[fields.InputDataFields.groundtruth_group_of]) + + def testDecodeObjectWeight(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + object_weights = [0.75, 1.0] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/weight': + dataset_util.float_list_feature(object_weights), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_weights].get_shape().as_list()), + [None]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + + self.assertAllEqual(object_weights, + tensor_dict[fields.InputDataFields.groundtruth_weights]) + + def testDecodeClassConfidence(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + class_confidence = [0.0, 1.0, 0.0] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/class/confidence': + dataset_util.float_list_feature(class_confidence), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual( + (output[fields.InputDataFields.groundtruth_image_confidences] + .get_shape().as_list()), [3]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + class_confidence, + tensor_dict[fields.InputDataFields.groundtruth_image_confidences]) + + def testDecodeInstanceSegmentation(self): + num_instances = 4 + image_height = 5 + image_width = 3 + + # Randomly generate image. + image_tensor = np.random.randint( + 256, size=(image_height, image_width, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + + # Randomly generate instance segmentation masks. + instance_masks = ( + np.random.randint(2, size=(num_instances, image_height, + image_width)).astype(np.float32)) + instance_masks_flattened = np.reshape(instance_masks, [-1]) + + # Randomly generate class labels for each instance. + object_classes = np.random.randint( + 100, size=(num_instances)).astype(np.int64) + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/height': + dataset_util.int64_feature(image_height), + 'image/width': + dataset_util.int64_feature(image_width), + 'image/object/mask': + dataset_util.float_list_feature(instance_masks_flattened), + 'image/object/class/label': + dataset_util.int64_list_feature(object_classes) + })).SerializeToString() + example_decoder = tf_example_decoder.TfExampleDecoder( + load_instance_masks=True) + output = example_decoder.decode(tf.convert_to_tensor(example)) + + self.assertAllEqual( + (output[fields.InputDataFields.groundtruth_instance_masks].get_shape( + ).as_list()), [4, 5, 3]) + + self.assertAllEqual((output[ + fields.InputDataFields.groundtruth_classes].get_shape().as_list()), + [4]) + return output + + tensor_dict = self.execute_cpu(graph_fn, []) + + self.assertAllEqual( + instance_masks.astype(np.float32), + tensor_dict[fields.InputDataFields.groundtruth_instance_masks]) + self.assertAllEqual(object_classes, + tensor_dict[fields.InputDataFields.groundtruth_classes]) + + def testInstancesNotAvailableByDefault(self): + num_instances = 4 + image_height = 5 + image_width = 3 + # Randomly generate image. + image_tensor = np.random.randint( + 256, size=(image_height, image_width, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + + # Randomly generate instance segmentation masks. + instance_masks = ( + np.random.randint(2, size=(num_instances, image_height, + image_width)).astype(np.float32)) + instance_masks_flattened = np.reshape(instance_masks, [-1]) + + # Randomly generate class labels for each instance. + object_classes = np.random.randint( + 100, size=(num_instances)).astype(np.int64) + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/height': + dataset_util.int64_feature(image_height), + 'image/width': + dataset_util.int64_feature(image_width), + 'image/object/mask': + dataset_util.float_list_feature(instance_masks_flattened), + 'image/object/class/label': + dataset_util.int64_list_feature(object_classes) + })).SerializeToString() + example_decoder = tf_example_decoder.TfExampleDecoder() + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertNotIn(fields.InputDataFields.groundtruth_instance_masks, + tensor_dict) + + def testDecodeImageLabels(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + + def graph_fn_1(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': dataset_util.bytes_feature(encoded_jpeg), + 'image/format': dataset_util.bytes_feature(six.b('jpeg')), + 'image/class/label': dataset_util.int64_list_feature([1, 2]), + })).SerializeToString() + example_decoder = tf_example_decoder.TfExampleDecoder() + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn_1, []) + self.assertIn(fields.InputDataFields.groundtruth_image_classes, tensor_dict) + self.assertAllEqual( + tensor_dict[fields.InputDataFields.groundtruth_image_classes], + np.array([1, 2])) + + def graph_fn_2(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/class/text': + dataset_util.bytes_list_feature( + [six.b('dog'), six.b('cat')]), + })).SerializeToString() + label_map_string = """ + item { + id:3 + name:'cat' + } + item { + id:1 + name:'dog' + } + """ + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn_2, []) + self.assertIn(fields.InputDataFields.groundtruth_image_classes, tensor_dict) + self.assertAllEqual( + tensor_dict[fields.InputDataFields.groundtruth_image_classes], + np.array([1, 3])) + + def testDecodeContextFeatures(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + num_features = 8 + context_feature_length = 10 + context_features = np.random.random(num_features*context_feature_length) + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/context_features': + dataset_util.float_list_feature(context_features), + 'image/context_feature_length': + dataset_util.int64_feature(context_feature_length), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder( + load_context_features=True) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertAllClose( + context_features.reshape(num_features, context_feature_length), + tensor_dict[fields.InputDataFields.context_features]) + self.assertAllEqual( + context_feature_length, + tensor_dict[fields.InputDataFields.context_feature_length]) + + def testContextFeaturesNotAvailableByDefault(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + num_features = 10 + context_feature_length = 10 + context_features = np.random.random(num_features*context_feature_length) + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/context_features': + dataset_util.float_list_feature(context_features), + 'image/context_feature_length': + dataset_util.int64_feature(context_feature_length), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder() + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + self.assertNotIn(fields.InputDataFields.context_features, + tensor_dict) + + def testExpandLabels(self): + label_map_string = """ + item { + id:1 + name:'cat' + ancestor_ids: 2 + } + item { + id:2 + name:'animal' + descendant_ids: 1 + } + item { + id:3 + name:'man' + ancestor_ids: 5 + } + item { + id:4 + name:'woman' + display_name:'woman' + ancestor_ids: 5 + } + item { + id:5 + name:'person' + descendant_ids: 3 + descendant_ids: 4 + } + """ + + label_map_path = os.path.join(self.get_temp_dir(), 'label_map.pbtxt') + with tf.gfile.Open(label_map_path, 'wb') as f: + f.write(label_map_string) + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0] + bbox_xmins = [1.0, 5.0] + bbox_ymaxs = [2.0, 6.0] + bbox_xmaxs = [3.0, 7.0] + bbox_classes_text = [six.b('cat'), six.b('cat')] + bbox_group_of = [0, 1] + image_class_text = [six.b('cat'), six.b('person')] + image_confidence = [1.0, 0.0] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + 'image/object/class/text': + dataset_util.bytes_list_feature(bbox_classes_text), + 'image/object/group_of': + dataset_util.int64_list_feature(bbox_group_of), + 'image/class/text': + dataset_util.bytes_list_feature(image_class_text), + 'image/class/confidence': + dataset_util.float_list_feature(image_confidence), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder( + label_map_proto_file=label_map_path, expand_hierarchy_labels=True) + return example_decoder.decode(tf.convert_to_tensor(example)) + + tensor_dict = self.execute_cpu(graph_fn, []) + + boxes = np.vstack([bbox_ymins, bbox_xmins, bbox_ymaxs, + bbox_xmaxs]).transpose() + expected_boxes = np.stack( + [boxes[0, :], boxes[0, :], boxes[1, :], boxes[1, :]], axis=0) + expected_boxes_class = np.array([1, 2, 1, 2]) + expected_boxes_group_of = np.array([0, 0, 1, 1]) + expected_image_class = np.array([1, 2, 3, 4, 5]) + expected_image_confidence = np.array([1.0, 1.0, 0.0, 0.0, 0.0]) + self.assertAllEqual(expected_boxes, + tensor_dict[fields.InputDataFields.groundtruth_boxes]) + self.assertAllEqual(expected_boxes_class, + tensor_dict[fields.InputDataFields.groundtruth_classes]) + self.assertAllEqual( + expected_boxes_group_of, + tensor_dict[fields.InputDataFields.groundtruth_group_of]) + self.assertAllEqual( + expected_image_class, + tensor_dict[fields.InputDataFields.groundtruth_image_classes]) + self.assertAllEqual( + expected_image_confidence, + tensor_dict[fields.InputDataFields.groundtruth_image_confidences]) + + def testDecodeDensePose(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0, 2.0] + bbox_xmins = [1.0, 5.0, 8.0] + bbox_ymaxs = [2.0, 6.0, 1.0] + bbox_xmaxs = [3.0, 7.0, 3.3] + densepose_num = [0, 4, 2] + densepose_part_index = [2, 2, 3, 4, 2, 9] + densepose_x = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6] + densepose_y = [0.9, 0.8, 0.7, 0.6, 0.5, 0.4] + densepose_u = [0.01, 0.02, 0.03, 0.04, 0.05, 0.06] + densepose_v = [0.99, 0.98, 0.97, 0.96, 0.95, 0.94] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + 'image/object/densepose/num': + dataset_util.int64_list_feature(densepose_num), + 'image/object/densepose/part_index': + dataset_util.int64_list_feature(densepose_part_index), + 'image/object/densepose/x': + dataset_util.float_list_feature(densepose_x), + 'image/object/densepose/y': + dataset_util.float_list_feature(densepose_y), + 'image/object/densepose/u': + dataset_util.float_list_feature(densepose_u), + 'image/object/densepose/v': + dataset_util.float_list_feature(densepose_v), + + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder( + load_dense_pose=True) + output = example_decoder.decode(tf.convert_to_tensor(example)) + dp_num_points = output[fields.InputDataFields.groundtruth_dp_num_points] + dp_part_ids = output[fields.InputDataFields.groundtruth_dp_part_ids] + dp_surface_coords = output[ + fields.InputDataFields.groundtruth_dp_surface_coords] + return dp_num_points, dp_part_ids, dp_surface_coords + + dp_num_points, dp_part_ids, dp_surface_coords = self.execute_cpu( + graph_fn, []) + + expected_dp_num_points = [0, 4, 2] + expected_dp_part_ids = [ + [0, 0, 0, 0], + [2, 2, 3, 4], + [2, 9, 0, 0] + ] + expected_dp_surface_coords = np.array( + [ + # Instance 0 (no points). + [[0., 0., 0., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.], + [0., 0., 0., 0.]], + # Instance 1 (4 points). + [[0.9, 0.1, 0.99, 0.01], + [0.8, 0.2, 0.98, 0.02], + [0.7, 0.3, 0.97, 0.03], + [0.6, 0.4, 0.96, 0.04]], + # Instance 2 (2 points). + [[0.5, 0.5, 0.95, 0.05], + [0.4, 0.6, 0.94, 0.06], + [0., 0., 0., 0.], + [0., 0., 0., 0.]], + ], dtype=np.float32) + + self.assertAllEqual(dp_num_points, expected_dp_num_points) + self.assertAllEqual(dp_part_ids, expected_dp_part_ids) + self.assertAllClose(dp_surface_coords, expected_dp_surface_coords) + + def testDecodeTrack(self): + image_tensor = np.random.randint(256, size=(4, 5, 3)).astype(np.uint8) + encoded_jpeg, _ = self._create_encoded_and_decoded_data( + image_tensor, 'jpeg') + bbox_ymins = [0.0, 4.0, 2.0] + bbox_xmins = [1.0, 5.0, 8.0] + bbox_ymaxs = [2.0, 6.0, 1.0] + bbox_xmaxs = [3.0, 7.0, 3.3] + track_labels = [0, 1, 2] + + def graph_fn(): + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(bbox_ymins), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(bbox_xmins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(bbox_ymaxs), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(bbox_xmaxs), + 'image/object/track/label': + dataset_util.int64_list_feature(track_labels), + })).SerializeToString() + + example_decoder = tf_example_decoder.TfExampleDecoder( + load_track_id=True) + output = example_decoder.decode(tf.convert_to_tensor(example)) + track_ids = output[fields.InputDataFields.groundtruth_track_ids] + return track_ids + + track_ids = self.execute_cpu(graph_fn, []) + + expected_track_labels = [0, 1, 2] + + self.assertAllEqual(track_ids, expected_track_labels) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.py b/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.py new file mode 100644 index 0000000..1565a91 --- /dev/null +++ b/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.py @@ -0,0 +1,314 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Sequence example decoder for object detection.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from six.moves import zip +import tensorflow.compat.v1 as tf +from tf_slim import tfexample_decoder as slim_example_decoder + +from object_detection.core import data_decoder +from object_detection.core import standard_fields as fields +from object_detection.utils import label_map_util + +# pylint: disable=g-import-not-at-top +try: + from tensorflow.contrib import lookup as contrib_lookup +except ImportError: + # TF 2.0 doesn't ship with contrib. + pass +# pylint: enable=g-import-not-at-top + + +class _ClassTensorHandler(slim_example_decoder.Tensor): + """An ItemHandler to fetch class ids from class text.""" + + def __init__(self, + tensor_key, + label_map_proto_file, + shape_keys=None, + shape=None, + default_value=''): + """Initializes the LookupTensor handler. + + Simply calls a vocabulary (most often, a label mapping) lookup. + + Args: + tensor_key: the name of the `TFExample` feature to read the tensor from. + label_map_proto_file: File path to a text format LabelMapProto message + mapping class text to id. + shape_keys: Optional name or list of names of the TF-Example feature in + which the tensor shape is stored. If a list, then each corresponds to + one dimension of the shape. + shape: Optional output shape of the `Tensor`. If provided, the `Tensor` is + reshaped accordingly. + default_value: The value used when the `tensor_key` is not found in a + particular `TFExample`. + + Raises: + ValueError: if both `shape_keys` and `shape` are specified. + """ + name_to_id = label_map_util.get_label_map_dict( + label_map_proto_file, use_display_name=False) + # We use a default_value of -1, but we expect all labels to be contained + # in the label map. + try: + # Dynamically try to load the tf v2 lookup, falling back to contrib + lookup = tf.compat.v2.lookup + hash_table_class = tf.compat.v2.lookup.StaticHashTable + except AttributeError: + lookup = contrib_lookup + hash_table_class = contrib_lookup.HashTable + name_to_id_table = hash_table_class( + initializer=lookup.KeyValueTensorInitializer( + keys=tf.constant(list(name_to_id.keys())), + values=tf.constant(list(name_to_id.values()), dtype=tf.int64)), + default_value=-1) + + self._name_to_id_table = name_to_id_table + super(_ClassTensorHandler, self).__init__(tensor_key, shape_keys, shape, + default_value) + + def tensors_to_item(self, keys_to_tensors): + unmapped_tensor = super(_ClassTensorHandler, + self).tensors_to_item(keys_to_tensors) + return self._name_to_id_table.lookup(unmapped_tensor) + + +class TfSequenceExampleDecoder(data_decoder.DataDecoder): + """Tensorflow Sequence Example proto decoder for Object Detection. + + Sequence examples contain sequences of images which share common + features. The structure of TfSequenceExamples can be seen in + dataset_tools/seq_example_util.py + + For the TFODAPI, the following fields are required: + Shared features: + 'image/format' + 'image/height' + 'image/width' + + Features with an entry for each image, where bounding box features can + be empty lists if the image does not contain any objects: + 'image/encoded' + 'image/source_id' + 'region/bbox/xmin' + 'region/bbox/xmax' + 'region/bbox/ymin' + 'region/bbox/ymax' + 'region/label/string' + + Optionally, the sequence example can include context_features for use in + Context R-CNN (see https://arxiv.org/abs/1912.03538): + 'image/context_features' + 'image/context_feature_length' + """ + + def __init__(self, + label_map_proto_file, + load_context_features=False, + use_display_name=False, + fully_annotated=False): + """Constructs `TfSequenceExampleDecoder` object. + + Args: + label_map_proto_file: a file path to a + object_detection.protos.StringIntLabelMap proto. The + label map will be used to map IDs of 'region/label/string'. + It is assumed that 'region/label/string' will be in the data. + load_context_features: Whether to load information from context_features, + to provide additional context to a detection model for training and/or + inference + use_display_name: whether or not to use the `display_name` for label + mapping (instead of `name`). Only used if label_map_proto_file is + provided. + fully_annotated: If True, will assume that every frame (whether it has + boxes or not), has been fully annotated. If False, a + 'region/is_annotated' field must be provided in the dataset which + indicates which frames have annotations. Default False. + """ + # Specifies how the tf.SequenceExamples are decoded. + self._context_keys_to_features = { + 'image/format': tf.FixedLenFeature((), tf.string, default_value='jpeg'), + 'image/height': tf.FixedLenFeature((), tf.int64), + 'image/width': tf.FixedLenFeature((), tf.int64), + } + self._sequence_keys_to_feature_lists = { + 'image/encoded': tf.FixedLenSequenceFeature([], dtype=tf.string), + 'image/source_id': tf.FixedLenSequenceFeature([], dtype=tf.string), + 'region/bbox/xmin': tf.VarLenFeature(dtype=tf.float32), + 'region/bbox/xmax': tf.VarLenFeature(dtype=tf.float32), + 'region/bbox/ymin': tf.VarLenFeature(dtype=tf.float32), + 'region/bbox/ymax': tf.VarLenFeature(dtype=tf.float32), + 'region/label/string': tf.VarLenFeature(dtype=tf.string), + 'region/label/confidence': tf.VarLenFeature(dtype=tf.float32), + } + + self._items_to_handlers = { + # Context. + fields.InputDataFields.image_height: + slim_example_decoder.Tensor('image/height'), + fields.InputDataFields.image_width: + slim_example_decoder.Tensor('image/width'), + + # Sequence. + fields.InputDataFields.num_groundtruth_boxes: + slim_example_decoder.NumBoxesSequence('region/bbox/xmin'), + fields.InputDataFields.groundtruth_boxes: + slim_example_decoder.BoundingBoxSequence( + prefix='region/bbox/', default_value=0.0), + fields.InputDataFields.groundtruth_weights: + slim_example_decoder.Tensor('region/label/confidence'), + } + + # If the dataset is sparsely annotated, parse sequence features which + # indicate which frames have been labeled. + if not fully_annotated: + self._sequence_keys_to_feature_lists['region/is_annotated'] = ( + tf.FixedLenSequenceFeature([], dtype=tf.int64)) + self._items_to_handlers[fields.InputDataFields.is_annotated] = ( + slim_example_decoder.Tensor('region/is_annotated')) + + self._items_to_handlers[fields.InputDataFields.image] = ( + slim_example_decoder.Tensor('image/encoded')) + self._items_to_handlers[fields.InputDataFields.source_id] = ( + slim_example_decoder.Tensor('image/source_id')) + + label_handler = _ClassTensorHandler( + 'region/label/string', label_map_proto_file, default_value='') + + self._items_to_handlers[ + fields.InputDataFields.groundtruth_classes] = label_handler + + if load_context_features: + self._context_keys_to_features['image/context_features'] = ( + tf.VarLenFeature(dtype=tf.float32)) + self._items_to_handlers[fields.InputDataFields.context_features] = ( + slim_example_decoder.ItemHandlerCallback( + ['image/context_features', 'image/context_feature_length'], + self._reshape_context_features)) + + self._context_keys_to_features['image/context_feature_length'] = ( + tf.FixedLenFeature((), tf.int64)) + self._items_to_handlers[fields.InputDataFields.context_feature_length] = ( + slim_example_decoder.Tensor('image/context_feature_length')) + self._fully_annotated = fully_annotated + + def decode(self, tf_seq_example_string_tensor): + """Decodes serialized `tf.SequenceExample`s and returns a tensor dictionary. + + Args: + tf_seq_example_string_tensor: a string tensor holding a serialized + `tf.SequenceExample`. + + Returns: + A list of dictionaries with (at least) the following tensors: + fields.InputDataFields.source_id: a [num_frames] string tensor with a + unique ID for each frame. + fields.InputDataFields.num_groundtruth_boxes: a [num_frames] int32 tensor + specifying the number of boxes in each frame. + fields.InputDataFields.groundtruth_boxes: a [num_frames, num_boxes, 4] + float32 tensor with bounding boxes for each frame. Note that num_boxes + is the maximum boxes seen in any individual frame. Any frames with fewer + boxes are padded with 0.0. + fields.InputDataFields.groundtruth_classes: a [num_frames, num_boxes] + int32 tensor with class indices for each box in each frame. + fields.InputDataFields.groundtruth_weights: a [num_frames, num_boxes] + float32 tensor with weights of the groundtruth boxes. + fields.InputDataFields.is_annotated: a [num_frames] bool tensor specifying + whether the image was annotated or not. If False, the corresponding + entries in the groundtruth tensor will be ignored. + fields.InputDataFields.context_features - 1D float32 tensor of shape + [context_feature_length * num_context_features] + fields.InputDataFields.context_feature_length - int32 tensor specifying + the length of each feature in context_features + fields.InputDataFields.image: a [num_frames] string tensor with + the encoded images. + """ + serialized_example = tf.reshape(tf_seq_example_string_tensor, shape=[]) + decoder = slim_example_decoder.TFSequenceExampleDecoder( + self._context_keys_to_features, self._sequence_keys_to_feature_lists, + self._items_to_handlers) + keys = decoder.list_items() + tensors = decoder.decode(serialized_example, items=keys) + tensor_dict = dict(list(zip(keys, tensors))) + tensor_dict[fields.InputDataFields.groundtruth_boxes].set_shape( + [None, None, 4]) + tensor_dict[fields.InputDataFields.num_groundtruth_boxes] = tf.cast( + tensor_dict[fields.InputDataFields.num_groundtruth_boxes], + dtype=tf.int32) + tensor_dict[fields.InputDataFields.groundtruth_classes] = tf.cast( + tensor_dict[fields.InputDataFields.groundtruth_classes], dtype=tf.int32) + tensor_dict[fields.InputDataFields.original_image_spatial_shape] = tf.cast( + tf.stack([ + tensor_dict[fields.InputDataFields.image_height], + tensor_dict[fields.InputDataFields.image_width] + ]), + dtype=tf.int32) + tensor_dict.pop(fields.InputDataFields.image_height) + tensor_dict.pop(fields.InputDataFields.image_width) + + def default_groundtruth_weights(): + """Produces weights of 1.0 for each valid box, and 0.0 otherwise.""" + num_boxes_per_frame = tensor_dict[ + fields.InputDataFields.num_groundtruth_boxes] + max_num_boxes = tf.reduce_max(num_boxes_per_frame) + num_boxes_per_frame_tiled = tf.tile( + tf.expand_dims(num_boxes_per_frame, axis=-1), + multiples=tf.stack([1, max_num_boxes])) + range_tiled = tf.tile( + tf.expand_dims(tf.range(max_num_boxes), axis=0), + multiples=tf.stack([tf.shape(num_boxes_per_frame)[0], 1])) + return tf.cast( + tf.greater(num_boxes_per_frame_tiled, range_tiled), tf.float32) + + tensor_dict[fields.InputDataFields.groundtruth_weights] = tf.cond( + tf.greater( + tf.size(tensor_dict[fields.InputDataFields.groundtruth_weights]), + 0), lambda: tensor_dict[fields.InputDataFields.groundtruth_weights], + default_groundtruth_weights) + + if self._fully_annotated: + tensor_dict[fields.InputDataFields.is_annotated] = tf.ones_like( + tensor_dict[fields.InputDataFields.num_groundtruth_boxes], + dtype=tf.bool) + else: + tensor_dict[fields.InputDataFields.is_annotated] = tf.cast( + tensor_dict[fields.InputDataFields.is_annotated], dtype=tf.bool) + + return tensor_dict + + def _reshape_context_features(self, keys_to_tensors): + """Reshape context features. + + The instance context_features are reshaped to + [num_context_features, context_feature_length] + + Args: + keys_to_tensors: a dictionary from keys to tensors. + + Returns: + A 2-D float tensor of shape [num_context_features, context_feature_length] + """ + context_feature_length = keys_to_tensors['image/context_feature_length'] + to_shape = tf.cast(tf.stack([-1, context_feature_length]), tf.int32) + context_features = keys_to_tensors['image/context_features'] + if isinstance(context_features, tf.SparseTensor): + context_features = tf.sparse_tensor_to_dense(context_features) + context_features = tf.reshape(context_features, to_shape) + return context_features diff --git a/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.pyc b/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92549d87c3c12f11e306432c74f8de3719e48fb8 GIT binary patch literal 11811 zcmcIq-E!PUc5cj&Ba)Ux{ZgVOTks~fC>J%9Jeo*SQQh&R-sN%CKnOA%7O2vF#aZSY?^>pbRmae;aJWc*?l;pj{Pxhn1 zFiZS6=@oHO`u(Exi|xInXHaDDUs~kd6g||EhliQRBihzQHZn<=4u(Z(Y{d*J;`BwT z(ZQCSMai&Ca})MQxeR2>+bI7&9j@E2^Y}7-M@Hz#uvMoyWMrbFrKCWcD37Bu4*O}6 z#de|vrXo1POISQ5>-pz!{b5jKQ_t04eOi2?-RXNpT^qn zm&L$+nq=Q}4SJ~_Nt|=Phn)Yv@qwy6PrbtAo-!@<+Vc)O>fl3V+G=k~?MdlBUSURDxQ||bEN9EYC*Z;Q>bbk&Z<}V2QHsi=7Ivl@&3A{_AaScSW8>Ino_T( z)oX6187Sl|)~SC1jd+}=CXKT6`$YR@H}Ri{z^s(~T`Q*U+?9{G~ymjbHRlk}so1l);BG4AXq)mTz^y4f3Ge(W^3?@1r?%A$*=3tjgSZ zG)ORtyl-u+ePOrA7GxQjQJGMzWfH|wZ~KZ^yKcC#4i3x06wuKuS@qZO^oNnzW%mek z?i+5lL1g?VRNCJ~!>`%gA0%2wJBb_2ujp?m4M#}hYC63e4HFWtSN*Ss+^-k>prqEc*z^zRt@Kfz;QE^&j`iT#UTTX1Sk4RPmxL z6Fn?)D7h)>(TW_lB^`jnAgm%lMm$1)gY;r#h9l$KS!*eXh_+;IaP>tRC$Y@#Z*ABr z5LizNLdcZZk9r_0hV-+8YPvY-N2APyFQRM&v20+S^6HN?I=tk1xc05FQfzVUc|mcF z^4L#vKdNUMMx{x6)IJbWYl_pcAWC&oYuPuP@Qbo6%2hw@``ZOnVN2)~;cfX)a#w(N zsF@xny%Yw{N!rkOgUB#W^)QPLLdw{@gTl1lJEfroqb?Yl6j_)yG*7WP7$(@?cxy5s z{XhI!U%HI);1nL%vr6qGCLAkqoc4?iu@+@Ifgz&3mf?HF017Q7FYX0&0OB5=ng~4k zGSa&n(RP*?`hf?=l<78nhA=>idPUB%(3xghf^nZkeEl{#5T029j}1f#(*~PaDl$IsJa*7qZ;E^RM6b~UH9yhdAOK7O%q1W-YvRe$2 zmAztnWdMhr=@sj(;Uk^5UTL~ay<+;IcFsPW@Uq?ELEvLZIy=o5wb=3I+wIo8x8R-e zI`W(Ku6Z}T+19Lg)$39CZTAyDMY`ap3~al#^YAI+I1cdw|0S*pE)1^Zsd(dcahAai z^s~&TJGXmH3wz)`gZEiWAun;V-~%>cJ1RCCx%N^v%4y&eh*re(cDY4PeT$#w?o!~; z1#f+c?@otAcA>~S3?ZLUmV}|<#tOr@=;59Fa~(!e7$0!FF~#!sHiS$q{|G-#b?$hb z>9*JQPTw+D@ww5ju%Pn^k1P`Gp-%pgV!=cNI898Hw3Yn_eDZ_EL@kGjVhfHs1V`+z z3kcHg<9=4W^s6cT$`8p_Is!t!vLN(PrPg9o>wZp?HQY{aBADb#$HmE|12|9(V@^+o zeF$R;TQw-?_U#79yy=l?XnguQ3Mhb_6k>2ZHm(Z_C)rbbR2_Ha#3k+%EQO zGROp(6pTm)LvtX$A8^(pBpJYu3ox8cvJyy?AGkoG_JEFL#2v6)@dVAf7?nMQ6yx$T z*#Tx)*#<2u`-3#EI~=Wu_D`%hcvFvq!SNnq(^sGjSiA780#ez56<(!Yb-YByNb_Dc zim6*U;apg$mM|Bd+G@;0Tk8k6AFi+aOHd_$*O;MRU0I3B{q#k*D0fy6Qmx$m&E0$5 zJD>dOlV9Jml6U-?9NsSXH;)?i+yhwC`NZ@tL z!>0Yz)m@dkHjz6bfU0z{Q97+YCCZ;t>5P<4s~td#jvEtE0pbDacf4Z8Rv=`s%^5g& z@$8AEb0?O*b7JY~iBc>IbIpJ$ACCh*j!RbYkhe z0 zT;KpKy;RY8S>4a!d@QM0*+lh1-F4<-Gm+UO>@-4oscC6>WM&&s&(si^$56t!MSdrWqScqH2%Pp`0%j;$!O0G8e-+nBpv>Tal21As@;fKN8n<(;2rT7O9F|d}J3Gj3AjX8o*JQ zT||#3_Nb?TYosTl+p0#%iYN{zjmoP3?QVjWa9jlI-ZY6KP6#Zh9L;J}X684~LoIjF6pjC6htVD!rA^3O`Gq zgZXK0xzMd!r#A|P+zsYf=j^o16yY&QHM0^A!WJDLf-CYEUwSRA7zoF#f& z5eySZvX6nC?dG6^X0a`)VS{Esbrcyu!p94v{bPlM3MUkzCG&+RY)E}&vK+eJhx$Wj zB{CkCNk83}VVeDPohbrW%u#Mr;s}U+VUX4USeG`?9u^IXR@ki#9dxlhuW6_C$ zLx%8L0&Jb7gBn%4gs>240|0s8J@_;96gHmdUjpuIk=Misl9RHQ)^TO5!fzuM_Cd$@_~|e4 zP*>aDbtF5kx7yw!{w~yiueaOnh1SB<0umn!t?SdB){WMU_VpH9UT9xzb@0~lX1zs} z-LQ!f>N+uZn+g1Lz}gn=SbT-8&jfypnS5kLEbQUa!mW;a-ByPvN5X^%RUHEE;C)8& z{{T`%r-Wxv3#1pI(=L%NsTJV9e^TlnF{5BtB=8rBQUMFQ~=wh*#7iU?GS&dBUseKHBafzOeK!2)##& zu1L{8z?xu?Y4!Kukc}QJmb@nGxZarmJ+;XBz}S{@-A=I^4miN-F=;@T6vO+f{4e3q zjYi#=L;g_?eE>+hEK@d_^g+#}rTgE~jZY@i+m|+I(MaKdoVR$uS~ZTTl(PTVxXNqr4`i_WUQ2RHe2p zO;v{CL4D9r)0Pr2Fy*iWjG85pHn#+1jME@?HFb8tn)L33Iq%jcki>sS6Kw&=vx$}1 z^hQnlC{J<12~<7KbV$Dn1ixXn6J5ZunUP!mi{pT? zB;WFLsA$@F={-TgGDo-WY1#kfvzi3wX)EChOPVPih=hQ+L5IIy7y@U=s2;K&4v_{| zf_zCj7!BMyTq+Ejc>vtV6W@4*11GyI{{c#^e5~;M$;)I6>@6pljvFFQ;BgBTyFOCOq(2^!S&cXj2dQK9L>t3CVHAc4l z{t3=B!Q%u8&oDrdRk|?N&RV=x*g+W!-1J`}#Hiy67kM=IQC{?Re?&KKknHaVT_ufE+$mN(|kJ@lLD9H*{ zyGZOqAQOu&2|`oGWD;HV@S^y|G~q=;mw(3#{Uf}hr}kQE@em*OAcr7E9U_Z`Phb%| zJA^XKAJM&g02?ekML!SRV|~8EJ&i7G17u@w7>GY__GPWyA)x=B7N0sE$W8zRghl{C zKt__K~8nY*aYHuG>y5<5IDon!VdX3lugQ)IJWJ| zNewKYmLpO@;{%)tk#6j-B9?s$*6KI{-v5fMaBoJ9qCOx=KIX>FKg#&~0%3q^UPE-W**Cso}S*G=i z0;e%ySl3AuP=AeEh9oDJ=tyR@-}cIkfOUK9Nr%3KS=CzrO8t(GeRe^Iq+Gc zVFlEE#+%21+hrU>cD(n!5AhTUi2jm3o=uAW@(B2d{#GZCZBztfSv|t1tW4G%Y*n9c zW1%w`{C*YKa)DQ=wLE*K+hjSB%lw01OXO56Y|hC&7GUL1VKZd7cIi#`Db5b#V}h`^z!Mi$_C zA`R%ff;H4Fku&xPnDhM@yp5#*A96gc!5M4SUZd%sPz;4LUX(8mma z!cTM2>c(ZL?`ipa8RzTg@VA5C0-hh?$CGyipWa39Vr%{^zZY=Qd=a52TP-5+{HWEV zzSCroopQWW;>8A=i;OeHeDjGj9GJBh{Ll-h^5N61o4O}~%^n&g%RFn{T?dtxJ z;Qh(A%agsGYkp=AJexPWsw7htYWPtjc(Pe-HemwUHF;BfbMLl;ACut2#w0(VM&4li z$uyq$oFB;pF43Nd;jo)kHYUxXm;(4L$7jheJ;fy@oot9Xm;4SkDGFRn$omvXTZt zFcom`TFaa6cL&9bM2l^f^ruxsK;4&Jd%>#v;%;z>9oP%+0xxJ4X$z4n4QK(Z(_j{J z@;(qoDU*PfA|S*Q2PkTAtRZhO=}5D>t9XsR1t$by_s7=kUBBPrSF=-X-r$YieroFa94nn1mew literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder_test.py b/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder_test.py new file mode 100644 index 0000000..2ea1c61 --- /dev/null +++ b/workspace/virtuallab/object_detection/data_decoders/tf_sequence_example_decoder_test.py @@ -0,0 +1,173 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for tf_sequence_example_decoder.py.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import numpy as np +import tensorflow.compat.v1 as tf + +from object_detection.core import standard_fields as fields +from object_detection.data_decoders import tf_sequence_example_decoder +from object_detection.dataset_tools import seq_example_util +from object_detection.utils import test_case + + +class TfSequenceExampleDecoderTest(test_case.TestCase): + + def _create_label_map(self, path): + label_map_text = """ + item { + name: "dog" + id: 1 + } + item { + name: "cat" + id: 2 + } + item { + name: "panda" + id: 4 + } + """ + with tf.gfile.Open(path, 'wb') as f: + f.write(label_map_text) + + def _make_random_serialized_jpeg_images(self, num_frames, image_height, + image_width): + def graph_fn(): + images = tf.cast(tf.random.uniform( + [num_frames, image_height, image_width, 3], + maxval=256, + dtype=tf.int32), dtype=tf.uint8) + images_list = tf.unstack(images, axis=0) + return [tf.io.encode_jpeg(image) for image in images_list] + encoded_images = self.execute(graph_fn, []) + return encoded_images + + def test_decode_sequence_example(self): + num_frames = 4 + image_height = 20 + image_width = 30 + + expected_groundtruth_boxes = [ + [[0.0, 0.0, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]], + [[0.2, 0.2, 1.0, 1.0], [0.0, 0.0, 1.0, 1.0]], + [[0.0, 0.0, 1.0, 1.0], [0.1, 0.1, 0.2, 0.2]], + [[0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]] + ] + expected_groundtruth_classes = [ + [-1, -1], + [-1, 1], + [1, 2], + [-1, -1] + ] + + flds = fields.InputDataFields + encoded_images = self._make_random_serialized_jpeg_images( + num_frames, image_height, image_width) + + def graph_fn(): + label_map_proto_file = os.path.join(self.get_temp_dir(), 'labelmap.pbtxt') + self._create_label_map(label_map_proto_file) + decoder = tf_sequence_example_decoder.TfSequenceExampleDecoder( + label_map_proto_file=label_map_proto_file) + sequence_example_serialized = seq_example_util.make_sequence_example( + dataset_name='video_dataset', + video_id='video', + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + image_format='JPEG', + image_source_ids=[str(i) for i in range(num_frames)], + is_annotated=[[1], [1], [1], [1]], + bboxes=[ + [[0., 0., 1., 1.]], # Frame 0. + [[0.2, 0.2, 1., 1.], + [0., 0., 1., 1.]], # Frame 1. + [[0., 0., 1., 1.], # Frame 2. + [0.1, 0.1, 0.2, 0.2]], + [[]], # Frame 3. + ], + label_strings=[ + ['fox'], # Frame 0. Fox will be filtered out. + ['fox', 'dog'], # Frame 1. Fox will be filtered out. + ['dog', 'cat'], # Frame 2. + [], # Frame 3 + ]).SerializeToString() + + example_string_tensor = tf.convert_to_tensor(sequence_example_serialized) + return decoder.decode(example_string_tensor) + + tensor_dict_out = self.execute(graph_fn, []) + self.assertAllClose(expected_groundtruth_boxes, + tensor_dict_out[flds.groundtruth_boxes]) + self.assertAllEqual(expected_groundtruth_classes, + tensor_dict_out[flds.groundtruth_classes]) + + def test_decode_sequence_example_negative_clip(self): + num_frames = 4 + image_height = 20 + image_width = 30 + + expected_groundtruth_boxes = -1 * np.ones((4, 0, 4)) + expected_groundtruth_classes = -1 * np.ones((4, 0)) + + flds = fields.InputDataFields + + encoded_images = self._make_random_serialized_jpeg_images( + num_frames, image_height, image_width) + + def graph_fn(): + sequence_example_serialized = seq_example_util.make_sequence_example( + dataset_name='video_dataset', + video_id='video', + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + image_format='JPEG', + image_source_ids=[str(i) for i in range(num_frames)], + bboxes=[ + [[]], + [[]], + [[]], + [[]] + ], + label_strings=[ + [], + [], + [], + [] + ]).SerializeToString() + example_string_tensor = tf.convert_to_tensor(sequence_example_serialized) + + label_map_proto_file = os.path.join(self.get_temp_dir(), 'labelmap.pbtxt') + self._create_label_map(label_map_proto_file) + decoder = tf_sequence_example_decoder.TfSequenceExampleDecoder( + label_map_proto_file=label_map_proto_file) + return decoder.decode(example_string_tensor) + + tensor_dict_out = self.execute(graph_fn, []) + self.assertAllClose(expected_groundtruth_boxes, + tensor_dict_out[flds.groundtruth_boxes]) + self.assertAllEqual(expected_groundtruth_classes, + tensor_dict_out[flds.groundtruth_classes]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/__init__.py b/workspace/virtuallab/object_detection/dataset_tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/__init__.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples.py new file mode 100644 index 0000000..a5b8b0a --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples.py @@ -0,0 +1,938 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""A Beam job to add contextual memory banks to tf.Examples. + +This tool groups images containing bounding boxes and embedded context features +by a key, either `image/location` or `image/seq_id`, and time horizon, +then uses these groups to build up a contextual memory bank from the embedded +context features from each image in the group and adds that context to the +output tf.Examples for each image in the group. + +Steps to generate a dataset with context from one with bounding boxes and +embedded context features: +1. Use object/detection/export_inference_graph.py to get a `saved_model` for + inference. The input node must accept a tf.Example proto. +2. Run this tool with `saved_model` from step 1 and a TFRecord of tf.Example + protos containing images, bounding boxes, and embedded context features. + The context features can be added to tf.Examples using + generate_embedding_data.py. + +Example Usage: +-------------- +python add_context_to_examples.py \ + --input_tfrecord path/to/input_tfrecords* \ + --output_tfrecord path/to/output_tfrecords \ + --sequence_key image/location \ + --time_horizon month + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import copy +import datetime +import io +import itertools +import json +import os +import numpy as np +import PIL.Image +import six +import tensorflow as tf + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +class ReKeyDataFn(beam.DoFn): + """Re-keys tfrecords by sequence_key. + + This Beam DoFn re-keys the tfrecords by a user-defined sequence_key + """ + + def __init__(self, sequence_key, time_horizon, + reduce_image_size, max_image_dimension): + """Initialization function. + + Args: + sequence_key: A feature name to use as a key for grouping sequences. + Must point to a key of type bytes_list + time_horizon: What length of time to use to partition the data when + building the memory banks. Options: `year`, `month`, `week`, `day `, + `hour`, `minute`, None + reduce_image_size: Whether to reduce the sizes of the stored images. + max_image_dimension: maximum dimension of reduced images + """ + self._sequence_key = sequence_key + if time_horizon is None or time_horizon in {'year', 'month', 'week', 'day', + 'hour', 'minute'}: + self._time_horizon = time_horizon + else: + raise ValueError('Time horizon not supported.') + self._reduce_image_size = reduce_image_size + self._max_image_dimension = max_image_dimension + self._session = None + self._num_examples_processed = beam.metrics.Metrics.counter( + 'data_rekey', 'num_tf_examples_processed') + self._num_images_resized = beam.metrics.Metrics.counter( + 'data_rekey', 'num_images_resized') + self._num_images_read = beam.metrics.Metrics.counter( + 'data_rekey', 'num_images_read') + self._num_images_found = beam.metrics.Metrics.counter( + 'data_rekey', 'num_images_read') + self._num_got_shape = beam.metrics.Metrics.counter( + 'data_rekey', 'num_images_got_shape') + self._num_images_found_size = beam.metrics.Metrics.counter( + 'data_rekey', 'num_images_found_size') + self._num_examples_cleared = beam.metrics.Metrics.counter( + 'data_rekey', 'num_examples_cleared') + self._num_examples_updated = beam.metrics.Metrics.counter( + 'data_rekey', 'num_examples_updated') + + def process(self, tfrecord_entry): + return self._rekey_examples(tfrecord_entry) + + def _largest_size_at_most(self, height, width, largest_side): + """Computes new shape with the largest side equal to `largest_side`. + + Args: + height: an int indicating the current height. + width: an int indicating the current width. + largest_side: A python integer indicating the size of + the largest side after resize. + Returns: + new_height: an int indicating the new height. + new_width: an int indicating the new width. + """ + + x_scale = float(largest_side) / float(width) + y_scale = float(largest_side) / float(height) + scale = min(x_scale, y_scale) + + new_width = int(width * scale) + new_height = int(height * scale) + + return new_height, new_width + + def _resize_image(self, input_example): + """Resizes the image within input_example and updates the height and width. + + Args: + input_example: A tf.Example that we want to update to contain a resized + image. + Returns: + input_example: Updated tf.Example. + """ + + original_image = copy.deepcopy( + input_example.features.feature['image/encoded'].bytes_list.value[0]) + self._num_images_read.inc(1) + + height = copy.deepcopy( + input_example.features.feature['image/height'].int64_list.value[0]) + + width = copy.deepcopy( + input_example.features.feature['image/width'].int64_list.value[0]) + + self._num_got_shape.inc(1) + + new_height, new_width = self._largest_size_at_most( + height, width, self._max_image_dimension) + + self._num_images_found_size.inc(1) + + encoded_jpg_io = io.BytesIO(original_image) + image = PIL.Image.open(encoded_jpg_io) + resized_image = image.resize((new_width, new_height)) + + with io.BytesIO() as output: + resized_image.save(output, format='JPEG') + encoded_resized_image = output.getvalue() + + self._num_images_resized.inc(1) + + del input_example.features.feature['image/encoded'].bytes_list.value[:] + del input_example.features.feature['image/height'].int64_list.value[:] + del input_example.features.feature['image/width'].int64_list.value[:] + + self._num_examples_cleared.inc(1) + + input_example.features.feature['image/encoded'].bytes_list.value.extend( + [encoded_resized_image]) + input_example.features.feature['image/height'].int64_list.value.extend( + [new_height]) + input_example.features.feature['image/width'].int64_list.value.extend( + [new_width]) + self._num_examples_updated.inc(1) + + return input_example + + def _rekey_examples(self, tfrecord_entry): + serialized_example = copy.deepcopy(tfrecord_entry) + + input_example = tf.train.Example.FromString(serialized_example) + + self._num_images_found.inc(1) + + if self._reduce_image_size: + input_example = self._resize_image(input_example) + self._num_images_resized.inc(1) + + new_key = input_example.features.feature[ + self._sequence_key].bytes_list.value[0] + + if self._time_horizon: + date_captured = datetime.datetime.strptime( + six.ensure_str(input_example.features.feature[ + 'image/date_captured'].bytes_list.value[0]), '%Y-%m-%d %H:%M:%S') + year = date_captured.year + month = date_captured.month + day = date_captured.day + week = np.floor(float(day) / float(7)) + hour = date_captured.hour + minute = date_captured.minute + + if self._time_horizon == 'year': + new_key = new_key + six.ensure_binary('/' + str(year)) + elif self._time_horizon == 'month': + new_key = new_key + six.ensure_binary( + '/' + str(year) + '/' + str(month)) + elif self._time_horizon == 'week': + new_key = new_key + six.ensure_binary( + '/' + str(year) + '/' + str(month) + '/' + str(week)) + elif self._time_horizon == 'day': + new_key = new_key + six.ensure_binary( + '/' + str(year) + '/' + str(month) + '/' + str(day)) + elif self._time_horizon == 'hour': + new_key = new_key + six.ensure_binary( + '/' + str(year) + '/' + str(month) + '/' + str(day) + '/' + ( + str(hour))) + elif self._time_horizon == 'minute': + new_key = new_key + six.ensure_binary( + '/' + str(year) + '/' + str(month) + '/' + str(day) + '/' + ( + str(hour) + '/' + str(minute))) + + self._num_examples_processed.inc(1) + + return [(new_key, input_example)] + + +class SortGroupedDataFn(beam.DoFn): + """Sorts data within a keyed group. + + This Beam DoFn sorts the grouped list of image examples by frame_num + """ + + def __init__(self, sequence_key, sorted_image_ids, + max_num_elements_in_context_features): + """Initialization function. + + Args: + sequence_key: A feature name to use as a key for grouping sequences. + Must point to a key of type bytes_list + sorted_image_ids: Whether the image ids are sortable to use as sorting + tie-breakers + max_num_elements_in_context_features: The maximum number of elements + allowed in the memory bank + """ + self._session = None + self._num_examples_processed = beam.metrics.Metrics.counter( + 'sort_group', 'num_groups_sorted') + self._too_many_elements = beam.metrics.Metrics.counter( + 'sort_group', 'too_many_elements') + self._split_elements = beam.metrics.Metrics.counter( + 'sort_group', 'split_elements') + self._sequence_key = six.ensure_binary(sequence_key) + self._sorted_image_ids = sorted_image_ids + self._max_num_elements_in_context_features = ( + max_num_elements_in_context_features) + + def process(self, grouped_entry): + return self._sort_image_examples(grouped_entry) + + def _sort_image_examples(self, grouped_entry): + key, example_collection = grouped_entry + example_list = list(example_collection) + + def get_frame_num(example): + return example.features.feature['image/seq_frame_num'].int64_list.value[0] + + def get_date_captured(example): + return datetime.datetime.strptime( + six.ensure_str( + example.features.feature[ + 'image/date_captured'].bytes_list.value[0]), + '%Y-%m-%d %H:%M:%S') + + def get_image_id(example): + return example.features.feature['image/source_id'].bytes_list.value[0] + + if self._sequence_key == six.ensure_binary('image/seq_id'): + sorting_fn = get_frame_num + elif self._sequence_key == six.ensure_binary('image/location'): + if self._sorted_image_ids: + sorting_fn = get_image_id + else: + sorting_fn = get_date_captured + + sorted_example_list = sorted(example_list, key=sorting_fn) + + self._num_examples_processed.inc(1) + + if len(sorted_example_list) > self._max_num_elements_in_context_features: + leftovers = sorted_example_list + output_list = [] + count = 0 + self._too_many_elements.inc(1) + while len(leftovers) > self._max_num_elements_in_context_features: + self._split_elements.inc(1) + new_key = key + six.ensure_binary('_' + str(count)) + new_list = leftovers[:self._max_num_elements_in_context_features] + output_list.append((new_key, new_list)) + leftovers = leftovers[:self._max_num_elements_in_context_features] + count += 1 + else: + output_list = [(key, sorted_example_list)] + + return output_list + + +def get_sliding_window(example_list, max_clip_length, stride_length): + """Yields a sliding window over data from example_list. + + Sliding window has width max_clip_len (n) and stride stride_len (m). + s -> (s0,s1,...s[n-1]), (s[m],s[m+1],...,s[m+n]), ... + + Args: + example_list: A list of examples. + max_clip_length: The maximum length of each clip. + stride_length: The stride between each clip. + + Yields: + A list of lists of examples, each with length <= max_clip_length + """ + + # check if the list is too short to slide over + if len(example_list) < max_clip_length: + yield example_list + else: + starting_values = [i*stride_length for i in + range(len(example_list)) if + len(example_list) > i*stride_length] + for start in starting_values: + result = tuple(itertools.islice(example_list, start, + min(start + max_clip_length, + len(example_list)))) + yield result + + +class GenerateContextFn(beam.DoFn): + """Generates context data for camera trap images. + + This Beam DoFn builds up contextual memory banks from groups of images and + stores them in the output tf.Example or tf.Sequence_example for each image. + """ + + def __init__(self, sequence_key, add_context_features, image_ids_to_keep, + keep_context_features_image_id_list=False, + subsample_context_features_rate=0, + keep_only_positives=False, + context_features_score_threshold=0.7, + keep_only_positives_gt=False, + max_num_elements_in_context_features=5000, + pad_context_features=False, + output_type='tf_example', max_clip_length=None, + context_feature_length=2057): + """Initialization function. + + Args: + sequence_key: A feature name to use as a key for grouping sequences. + add_context_features: Whether to keep and store the contextual memory + bank. + image_ids_to_keep: A list of image ids to save, to use to build data + subsets for evaluation. + keep_context_features_image_id_list: Whether to save an ordered list of + the ids of the images in the contextual memory bank. + subsample_context_features_rate: What rate to subsample images for the + contextual memory bank. + keep_only_positives: Whether to only keep high scoring + (>context_features_score_threshold) features in the contextual memory + bank. + context_features_score_threshold: What threshold to use for keeping + features. + keep_only_positives_gt: Whether to only keep features from images that + contain objects based on the ground truth (for training). + max_num_elements_in_context_features: the maximum number of elements in + the memory bank + pad_context_features: Whether to pad the context features to a fixed size. + output_type: What type of output, tf_example of tf_sequence_example + max_clip_length: The maximum length of a sequence example, before + splitting into multiple + context_feature_length: The length of the context feature embeddings + stored in the input data. + """ + self._session = None + self._num_examples_processed = beam.metrics.Metrics.counter( + 'sequence_data_generation', 'num_seq_examples_processed') + self._num_keys_processed = beam.metrics.Metrics.counter( + 'sequence_data_generation', 'num_keys_processed') + self._sequence_key = sequence_key + self._add_context_features = add_context_features + self._pad_context_features = pad_context_features + self._output_type = output_type + self._max_clip_length = max_clip_length + if six.ensure_str(image_ids_to_keep) == 'All': + self._image_ids_to_keep = None + else: + with tf.io.gfile.GFile(image_ids_to_keep) as f: + self._image_ids_to_keep = json.load(f) + self._keep_context_features_image_id_list = ( + keep_context_features_image_id_list) + self._subsample_context_features_rate = subsample_context_features_rate + self._keep_only_positives = keep_only_positives + self._keep_only_positives_gt = keep_only_positives_gt + self._context_features_score_threshold = context_features_score_threshold + self._max_num_elements_in_context_features = ( + max_num_elements_in_context_features) + self._context_feature_length = context_feature_length + + self._images_kept = beam.metrics.Metrics.counter( + 'sequence_data_generation', 'images_kept') + self._images_loaded = beam.metrics.Metrics.counter( + 'sequence_data_generation', 'images_loaded') + + def process(self, grouped_entry): + return self._add_context_to_example(copy.deepcopy(grouped_entry)) + + def _build_context_features(self, example_list): + context_features = [] + context_features_image_id_list = [] + count = 0 + example_embedding = [] + + for idx, example in enumerate(example_list): + if self._subsample_context_features_rate > 0: + if (idx % self._subsample_context_features_rate) != 0: + example.features.feature[ + 'context_features_idx'].int64_list.value.append( + self._max_num_elements_in_context_features + 1) + continue + if self._keep_only_positives: + if example.features.feature[ + 'image/embedding_score' + ].float_list.value[0] < self._context_features_score_threshold: + example.features.feature[ + 'context_features_idx'].int64_list.value.append( + self._max_num_elements_in_context_features + 1) + continue + if self._keep_only_positives_gt: + if len(example.features.feature[ + 'image/object/bbox/xmin' + ].float_list.value) < 1: + example.features.feature[ + 'context_features_idx'].int64_list.value.append( + self._max_num_elements_in_context_features + 1) + continue + + example_embedding = list(example.features.feature[ + 'image/embedding'].float_list.value) + context_features.extend(example_embedding) + example.features.feature[ + 'context_features_idx'].int64_list.value.append(count) + count += 1 + example_image_id = example.features.feature[ + 'image/source_id'].bytes_list.value[0] + context_features_image_id_list.append(example_image_id) + + if not example_embedding: + example_embedding.append(np.zeros(self._context_feature_length)) + + feature_length = self._context_feature_length + + # If the example_list is not empty and image/embedding_length is in the + # featture dict, feature_length will be assigned to that. Otherwise, it will + # be kept as default. + if example_list and ( + 'image/embedding_length' in example_list[0].features.feature): + feature_length = example_list[0].features.feature[ + 'image/embedding_length'].int64_list.value[0] + + if self._pad_context_features: + while len(context_features_image_id_list) < ( + self._max_num_elements_in_context_features): + context_features_image_id_list.append('') + + return context_features, feature_length, context_features_image_id_list + + def _add_context_to_example(self, grouped_entry): + key, example_collection = grouped_entry + list_of_examples = [] + + example_list = list(example_collection) + + if self._add_context_features: + context_features, feature_length, context_features_image_id_list = ( + self._build_context_features(example_list)) + + if self._image_ids_to_keep is not None: + new_example_list = [] + for example in example_list: + im_id = example.features.feature['image/source_id'].bytes_list.value[0] + self._images_loaded.inc(1) + if six.ensure_str(im_id) in self._image_ids_to_keep: + self._images_kept.inc(1) + new_example_list.append(example) + if new_example_list: + example_list = new_example_list + else: + return [] + + if self._output_type == 'tf_sequence_example': + if self._max_clip_length is not None: + # For now, no overlap + clips = get_sliding_window( + example_list, self._max_clip_length, self._max_clip_length) + else: + clips = [example_list] + + for clip_num, clip_list in enumerate(clips): + # initialize sequence example + seq_example = tf.train.SequenceExample() + video_id = six.ensure_str(key)+'_'+ str(clip_num) + seq_example.context.feature['clip/media_id'].bytes_list.value.append( + video_id.encode('utf8')) + seq_example.context.feature['clip/frames'].int64_list.value.append( + len(clip_list)) + + seq_example.context.feature[ + 'clip/start/timestamp'].int64_list.value.append(0) + seq_example.context.feature[ + 'clip/end/timestamp'].int64_list.value.append(len(clip_list)) + seq_example.context.feature['image/format'].bytes_list.value.append( + six.ensure_binary('JPG')) + seq_example.context.feature['image/channels'].int64_list.value.append(3) + context_example = clip_list[0] + seq_example.context.feature['image/height'].int64_list.value.append( + context_example.features.feature[ + 'image/height'].int64_list.value[0]) + seq_example.context.feature['image/width'].int64_list.value.append( + context_example.features.feature['image/width'].int64_list.value[0]) + + seq_example.context.feature[ + 'image/context_feature_length'].int64_list.value.append( + feature_length) + seq_example.context.feature[ + 'image/context_features'].float_list.value.extend( + context_features) + if self._keep_context_features_image_id_list: + seq_example.context.feature[ + 'image/context_features_image_id_list'].bytes_list.value.extend( + context_features_image_id_list) + + encoded_image_list = seq_example.feature_lists.feature_list[ + 'image/encoded'] + timestamps_list = seq_example.feature_lists.feature_list[ + 'image/timestamp'] + context_features_idx_list = seq_example.feature_lists.feature_list[ + 'image/context_features_idx'] + date_captured_list = seq_example.feature_lists.feature_list[ + 'image/date_captured'] + unix_time_list = seq_example.feature_lists.feature_list[ + 'image/unix_time'] + location_list = seq_example.feature_lists.feature_list['image/location'] + image_ids_list = seq_example.feature_lists.feature_list[ + 'image/source_id'] + gt_xmin_list = seq_example.feature_lists.feature_list[ + 'region/bbox/xmin'] + gt_xmax_list = seq_example.feature_lists.feature_list[ + 'region/bbox/xmax'] + gt_ymin_list = seq_example.feature_lists.feature_list[ + 'region/bbox/ymin'] + gt_ymax_list = seq_example.feature_lists.feature_list[ + 'region/bbox/ymax'] + gt_type_list = seq_example.feature_lists.feature_list[ + 'region/label/index'] + gt_type_string_list = seq_example.feature_lists.feature_list[ + 'region/label/string'] + gt_is_annotated_list = seq_example.feature_lists.feature_list[ + 'region/is_annotated'] + + for idx, example in enumerate(clip_list): + + encoded_image = encoded_image_list.feature.add() + encoded_image.bytes_list.value.extend( + example.features.feature['image/encoded'].bytes_list.value) + + image_id = image_ids_list.feature.add() + image_id.bytes_list.value.append( + example.features.feature['image/source_id'].bytes_list.value[0]) + + timestamp = timestamps_list.feature.add() + # Timestamp is currently order in the list. + timestamp.int64_list.value.extend([idx]) + + context_features_idx = context_features_idx_list.feature.add() + context_features_idx.int64_list.value.extend( + example.features.feature['context_features_idx'].int64_list.value) + + date_captured = date_captured_list.feature.add() + date_captured.bytes_list.value.extend( + example.features.feature['image/date_captured'].bytes_list.value) + unix_time = unix_time_list.feature.add() + unix_time.float_list.value.extend( + example.features.feature['image/unix_time'].float_list.value) + location = location_list.feature.add() + location.bytes_list.value.extend( + example.features.feature['image/location'].bytes_list.value) + + gt_xmin = gt_xmin_list.feature.add() + gt_xmax = gt_xmax_list.feature.add() + gt_ymin = gt_ymin_list.feature.add() + gt_ymax = gt_ymax_list.feature.add() + gt_type = gt_type_list.feature.add() + gt_type_str = gt_type_string_list.feature.add() + + gt_is_annotated = gt_is_annotated_list.feature.add() + gt_is_annotated.int64_list.value.append(1) + + gt_xmin.float_list.value.extend( + example.features.feature[ + 'image/object/bbox/xmin'].float_list.value) + gt_xmax.float_list.value.extend( + example.features.feature[ + 'image/object/bbox/xmax'].float_list.value) + gt_ymin.float_list.value.extend( + example.features.feature[ + 'image/object/bbox/ymin'].float_list.value) + gt_ymax.float_list.value.extend( + example.features.feature[ + 'image/object/bbox/ymax'].float_list.value) + + gt_type.int64_list.value.extend( + example.features.feature[ + 'image/object/class/label'].int64_list.value) + gt_type_str.bytes_list.value.extend( + example.features.feature[ + 'image/object/class/text'].bytes_list.value) + + self._num_examples_processed.inc(1) + list_of_examples.append(seq_example) + + elif self._output_type == 'tf_example': + + for example in example_list: + im_id = example.features.feature['image/source_id'].bytes_list.value[0] + + if self._add_context_features: + example.features.feature[ + 'image/context_features'].float_list.value.extend( + context_features) + example.features.feature[ + 'image/context_feature_length'].int64_list.value.append( + feature_length) + + if self._keep_context_features_image_id_list: + example.features.feature[ + 'image/context_features_image_id_list'].bytes_list.value.extend( + context_features_image_id_list) + + self._num_examples_processed.inc(1) + list_of_examples.append(example) + + return list_of_examples + + +def construct_pipeline(pipeline, + input_tfrecord, + output_tfrecord, + sequence_key, + time_horizon=None, + subsample_context_features_rate=0, + reduce_image_size=True, + max_image_dimension=1024, + add_context_features=True, + sorted_image_ids=True, + image_ids_to_keep='All', + keep_context_features_image_id_list=False, + keep_only_positives=False, + context_features_score_threshold=0.7, + keep_only_positives_gt=False, + max_num_elements_in_context_features=5000, + num_shards=0, + output_type='tf_example', + max_clip_length=None, + context_feature_length=2057): + """Returns a beam pipeline to run object detection inference. + + Args: + pipeline: Initialized beam pipeline. + input_tfrecord: An TFRecord of tf.train.Example protos containing images. + output_tfrecord: An TFRecord of tf.train.Example protos that contain images + in the input TFRecord and the detections from the model. + sequence_key: A feature name to use as a key for grouping sequences. + time_horizon: What length of time to use to partition the data when building + the memory banks. Options: `year`, `month`, `week`, `day `, `hour`, + `minute`, None. + subsample_context_features_rate: What rate to subsample images for the + contextual memory bank. + reduce_image_size: Whether to reduce the size of the stored images. + max_image_dimension: The maximum image dimension to use for resizing. + add_context_features: Whether to keep and store the contextual memory bank. + sorted_image_ids: Whether the image ids are sortable, and can be used as + datetime tie-breakers when building memory banks. + image_ids_to_keep: A list of image ids to save, to use to build data subsets + for evaluation. + keep_context_features_image_id_list: Whether to save an ordered list of the + ids of the images in the contextual memory bank. + keep_only_positives: Whether to only keep high scoring + (>context_features_score_threshold) features in the contextual memory + bank. + context_features_score_threshold: What threshold to use for keeping + features. + keep_only_positives_gt: Whether to only keep features from images that + contain objects based on the ground truth (for training). + max_num_elements_in_context_features: the maximum number of elements in the + memory bank + num_shards: The number of output shards. + output_type: What type of output, tf_example of tf_sequence_example + max_clip_length: The maximum length of a sequence example, before + splitting into multiple + context_feature_length: The length of the context feature embeddings stored + in the input data. + """ + if output_type == 'tf_example': + coder = beam.coders.ProtoCoder(tf.train.Example) + elif output_type == 'tf_sequence_example': + coder = beam.coders.ProtoCoder(tf.train.SequenceExample) + else: + raise ValueError('Unsupported output type.') + input_collection = ( + pipeline | 'ReadInputTFRecord' >> beam.io.tfrecordio.ReadFromTFRecord( + input_tfrecord, + coder=beam.coders.BytesCoder())) + rekey_collection = input_collection | 'RekeyExamples' >> beam.ParDo( + ReKeyDataFn(sequence_key, time_horizon, + reduce_image_size, max_image_dimension)) + grouped_collection = ( + rekey_collection | 'GroupBySequenceKey' >> beam.GroupByKey()) + grouped_collection = ( + grouped_collection | 'ReshuffleGroups' >> beam.Reshuffle()) + ordered_collection = ( + grouped_collection | 'OrderByFrameNumber' >> beam.ParDo( + SortGroupedDataFn(sequence_key, sorted_image_ids, + max_num_elements_in_context_features))) + ordered_collection = ( + ordered_collection | 'ReshuffleSortedGroups' >> beam.Reshuffle()) + output_collection = ( + ordered_collection | 'AddContextToExamples' >> beam.ParDo( + GenerateContextFn( + sequence_key, add_context_features, image_ids_to_keep, + keep_context_features_image_id_list=( + keep_context_features_image_id_list), + subsample_context_features_rate=subsample_context_features_rate, + keep_only_positives=keep_only_positives, + keep_only_positives_gt=keep_only_positives_gt, + context_features_score_threshold=( + context_features_score_threshold), + max_num_elements_in_context_features=( + max_num_elements_in_context_features), + output_type=output_type, + max_clip_length=max_clip_length, + context_feature_length=context_feature_length))) + + output_collection = ( + output_collection | 'ReshuffleExamples' >> beam.Reshuffle()) + _ = output_collection | 'WritetoDisk' >> beam.io.tfrecordio.WriteToTFRecord( + output_tfrecord, + num_shards=num_shards, + coder=coder) + + +def parse_args(argv): + """Command-line argument parser. + + Args: + argv: command line arguments + Returns: + beam_args: Arguments for the beam pipeline. + pipeline_args: Arguments for the pipeline options, such as runner type. + """ + parser = argparse.ArgumentParser() + parser.add_argument( + '--input_tfrecord', + dest='input_tfrecord', + required=True, + help='TFRecord containing images in tf.Example format for object ' + 'detection, with bounding boxes and contextual feature embeddings.') + parser.add_argument( + '--output_tfrecord', + dest='output_tfrecord', + required=True, + help='TFRecord containing images in tf.Example format, with added ' + 'contextual memory banks.') + parser.add_argument( + '--sequence_key', + dest='sequence_key', + default='image/location', + help='Key to use when grouping sequences: so far supports `image/seq_id` ' + 'and `image/location`.') + parser.add_argument( + '--context_feature_length', + dest='context_feature_length', + default=2057, + help='The length of the context feature embeddings stored in the input ' + 'data.') + parser.add_argument( + '--time_horizon', + dest='time_horizon', + default=None, + help='What time horizon to use when splitting the data, if any. Options ' + 'are: `year`, `month`, `week`, `day `, `hour`, `minute`, `None`.') + parser.add_argument( + '--subsample_context_features_rate', + dest='subsample_context_features_rate', + default=0, + help='Whether to subsample the context_features, and if so how many to ' + 'sample. If the rate is set to X, it will sample context from 1 out of ' + 'every X images. Default is sampling from every image, which is X=0.') + parser.add_argument( + '--reduce_image_size', + dest='reduce_image_size', + default=True, + help='downsamples images to have longest side max_image_dimension, ' + 'maintaining aspect ratio') + parser.add_argument( + '--max_image_dimension', + dest='max_image_dimension', + default=1024, + help='Sets max image dimension for resizing.') + parser.add_argument( + '--add_context_features', + dest='add_context_features', + default=True, + help='Adds a memory bank of embeddings to each clip') + parser.add_argument( + '--sorted_image_ids', + dest='sorted_image_ids', + default=True, + help='Whether the image source_ids are sortable to deal with ' + 'date_captured tie-breaks.') + parser.add_argument( + '--image_ids_to_keep', + dest='image_ids_to_keep', + default='All', + help='Path to .json list of image ids to keep, used for ground truth ' + 'eval creation.') + parser.add_argument( + '--keep_context_features_image_id_list', + dest='keep_context_features_image_id_list', + default=False, + help='Whether or not to keep a list of the image_ids corresponding to ' + 'the memory bank.') + parser.add_argument( + '--keep_only_positives', + dest='keep_only_positives', + default=False, + help='Whether or not to keep only positive boxes based on score.') + parser.add_argument( + '--context_features_score_threshold', + dest='context_features_score_threshold', + default=0.7, + help='What score threshold to use for boxes in context_features, when ' + '`keep_only_positives` is set to `True`.') + parser.add_argument( + '--keep_only_positives_gt', + dest='keep_only_positives_gt', + default=False, + help='Whether or not to keep only positive boxes based on gt class.') + parser.add_argument( + '--max_num_elements_in_context_features', + dest='max_num_elements_in_context_features', + default=2000, + help='Sets max number of context feature elements per memory bank. ' + 'If the number of images in the context group is greater than ' + '`max_num_elements_in_context_features`, the context group will be split.' + ) + parser.add_argument( + '--output_type', + dest='output_type', + default='tf_example', + help='Output type, one of `tf_example`, `tf_sequence_example`.') + parser.add_argument( + '--max_clip_length', + dest='max_clip_length', + default=None, + help='Max length for sequence example outputs.') + parser.add_argument( + '--num_shards', + dest='num_shards', + default=0, + help='Number of output shards.') + beam_args, pipeline_args = parser.parse_known_args(argv) + return beam_args, pipeline_args + + +def main(argv=None, save_main_session=True): + """Runs the Beam pipeline that performs inference. + + Args: + argv: Command line arguments. + save_main_session: Whether to save the main session. + """ + args, pipeline_args = parse_args(argv) + + pipeline_options = beam.options.pipeline_options.PipelineOptions( + pipeline_args) + pipeline_options.view_as( + beam.options.pipeline_options.SetupOptions).save_main_session = ( + save_main_session) + + dirname = os.path.dirname(args.output_tfrecord) + tf.io.gfile.makedirs(dirname) + + p = beam.Pipeline(options=pipeline_options) + + construct_pipeline( + p, + args.input_tfrecord, + args.output_tfrecord, + args.sequence_key, + args.time_horizon, + args.subsample_context_features_rate, + args.reduce_image_size, + args.max_image_dimension, + args.add_context_features, + args.sorted_image_ids, + args.image_ids_to_keep, + args.keep_context_features_image_id_list, + args.keep_only_positives, + args.context_features_score_threshold, + args.keep_only_positives_gt, + args.max_num_elements_in_context_features, + args.num_shards, + args.output_type, + args.max_clip_length, + args.context_feature_length) + + p.run() + + +if __name__ == '__main__': + main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples_tf2_test.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples_tf2_test.py new file mode 100644 index 0000000..ae4e02b --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/add_context_to_examples_tf2_test.py @@ -0,0 +1,396 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for add_context_to_examples.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import contextlib +import datetime +import os +import tempfile +import unittest + +import numpy as np +import six +import tensorflow as tf + +from object_detection.utils import tf_version + +if tf_version.is_tf2(): + from object_detection.dataset_tools.context_rcnn import add_context_to_examples # pylint:disable=g-import-not-at-top + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +@contextlib.contextmanager +def InMemoryTFRecord(entries): + temp = tempfile.NamedTemporaryFile(delete=False) + filename = temp.name + try: + with tf.io.TFRecordWriter(filename) as writer: + for value in entries: + writer.write(value) + yield filename + finally: + os.unlink(temp.name) + + +def BytesFeature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) + + +def BytesListFeature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=value)) + + +def Int64Feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) + + +def Int64ListFeature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=value)) + + +def FloatListFeature(value): + return tf.train.Feature(float_list=tf.train.FloatList(value=value)) + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class GenerateContextDataTest(tf.test.TestCase): + + def _create_first_tf_example(self): + encoded_image = tf.io.encode_jpeg( + tf.constant(np.ones((4, 4, 3)).astype(np.uint8))).numpy() + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': BytesFeature(encoded_image), + 'image/source_id': BytesFeature(six.ensure_binary('image_id_1')), + 'image/height': Int64Feature(4), + 'image/width': Int64Feature(4), + 'image/object/class/label': Int64ListFeature([5, 5]), + 'image/object/class/text': BytesListFeature([six.ensure_binary('hyena'), + six.ensure_binary('hyena') + ]), + 'image/object/bbox/xmin': FloatListFeature([0.0, 0.1]), + 'image/object/bbox/xmax': FloatListFeature([0.2, 0.3]), + 'image/object/bbox/ymin': FloatListFeature([0.4, 0.5]), + 'image/object/bbox/ymax': FloatListFeature([0.6, 0.7]), + 'image/seq_id': BytesFeature(six.ensure_binary('01')), + 'image/seq_num_frames': Int64Feature(2), + 'image/seq_frame_num': Int64Feature(0), + 'image/date_captured': BytesFeature( + six.ensure_binary(str(datetime.datetime(2020, 1, 1, 1, 0, 0)))), + 'image/embedding': FloatListFeature([0.1, 0.2, 0.3]), + 'image/embedding_score': FloatListFeature([0.9]), + 'image/embedding_length': Int64Feature(3) + + })) + + return example.SerializeToString() + + def _create_second_tf_example(self): + encoded_image = tf.io.encode_jpeg( + tf.constant(np.ones((4, 4, 3)).astype(np.uint8))).numpy() + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': BytesFeature(encoded_image), + 'image/source_id': BytesFeature(six.ensure_binary('image_id_2')), + 'image/height': Int64Feature(4), + 'image/width': Int64Feature(4), + 'image/object/class/label': Int64ListFeature([5]), + 'image/object/class/text': BytesListFeature([six.ensure_binary('hyena') + ]), + 'image/object/bbox/xmin': FloatListFeature([0.0]), + 'image/object/bbox/xmax': FloatListFeature([0.1]), + 'image/object/bbox/ymin': FloatListFeature([0.2]), + 'image/object/bbox/ymax': FloatListFeature([0.3]), + 'image/seq_id': BytesFeature(six.ensure_binary('01')), + 'image/seq_num_frames': Int64Feature(2), + 'image/seq_frame_num': Int64Feature(1), + 'image/date_captured': BytesFeature( + six.ensure_binary(str(datetime.datetime(2020, 1, 1, 1, 1, 0)))), + 'image/embedding': FloatListFeature([0.4, 0.5, 0.6]), + 'image/embedding_score': FloatListFeature([0.9]), + 'image/embedding_length': Int64Feature(3) + })) + + return example.SerializeToString() + + def assert_expected_examples(self, tf_example_list): + self.assertAllEqual( + {tf_example.features.feature['image/source_id'].bytes_list.value[0] + for tf_example in tf_example_list}, + {six.ensure_binary('image_id_1'), six.ensure_binary('image_id_2')}) + self.assertAllClose( + tf_example_list[0].features.feature[ + 'image/context_features'].float_list.value, + [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]) + self.assertAllClose( + tf_example_list[1].features.feature[ + 'image/context_features'].float_list.value, + [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]) + + def assert_expected_sequence_example(self, tf_sequence_example_list): + tf_sequence_example = tf_sequence_example_list[0] + num_frames = 2 + + self.assertAllEqual( + tf_sequence_example.context.feature[ + 'clip/media_id'].bytes_list.value[0], six.ensure_binary( + '01_0')) + self.assertAllClose( + tf_sequence_example.context.feature[ + 'image/context_features'].float_list.value, + [0.1, 0.2, 0.3, 0.4, 0.5, 0.6]) + + seq_feature_dict = tf_sequence_example.feature_lists.feature_list + + self.assertLen( + seq_feature_dict['image/encoded'].feature[:], + num_frames) + actual_timestamps = [ + feature.int64_list.value[0] for feature + in seq_feature_dict['image/timestamp'].feature] + timestamps = [0, 1] + self.assertAllEqual(timestamps, actual_timestamps) + + # First image. + self.assertAllClose( + [0.4, 0.5], + seq_feature_dict['region/bbox/ymin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.0, 0.1], + seq_feature_dict['region/bbox/xmin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.6, 0.7], + seq_feature_dict['region/bbox/ymax'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.2, 0.3], + seq_feature_dict['region/bbox/xmax'].feature[0].float_list.value[:]) + self.assertAllEqual( + [six.ensure_binary('hyena'), six.ensure_binary('hyena')], + seq_feature_dict['region/label/string'].feature[0].bytes_list.value[:]) + + # Second example. + self.assertAllClose( + [0.2], + seq_feature_dict['region/bbox/ymin'].feature[1].float_list.value[:]) + self.assertAllClose( + [0.0], + seq_feature_dict['region/bbox/xmin'].feature[1].float_list.value[:]) + self.assertAllClose( + [0.3], + seq_feature_dict['region/bbox/ymax'].feature[1].float_list.value[:]) + self.assertAllClose( + [0.1], + seq_feature_dict['region/bbox/xmax'].feature[1].float_list.value[:]) + self.assertAllEqual( + [six.ensure_binary('hyena')], + seq_feature_dict['region/label/string'].feature[1].bytes_list.value[:]) + + def assert_expected_key(self, key): + self.assertAllEqual(key, b'01') + + def assert_sorted(self, example_collection): + example_list = list(example_collection) + counter = 0 + for example in example_list: + frame_num = example.features.feature[ + 'image/seq_frame_num'].int64_list.value[0] + self.assertGreaterEqual(frame_num, counter) + counter = frame_num + + def assert_context(self, example_collection): + example_list = list(example_collection) + for example in example_list: + context = example.features.feature[ + 'image/context_features'].float_list.value + self.assertAllClose([0.1, 0.2, 0.3, 0.4, 0.5, 0.6], context) + + def assert_resized(self, example): + width = example.features.feature['image/width'].int64_list.value[0] + self.assertAllEqual(width, 2) + height = example.features.feature['image/height'].int64_list.value[0] + self.assertAllEqual(height, 2) + + def assert_size(self, example): + width = example.features.feature['image/width'].int64_list.value[0] + self.assertAllEqual(width, 4) + height = example.features.feature['image/height'].int64_list.value[0] + self.assertAllEqual(height, 4) + + def test_sliding_window(self): + example_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] + max_clip_length = 3 + stride_length = 3 + out_list = [list(i) for i in add_context_to_examples.get_sliding_window( + example_list, max_clip_length, stride_length)] + self.assertAllEqual(out_list, [['a', 'b', 'c'], + ['d', 'e', 'f'], + ['g']]) + + def test_rekey_data_fn(self): + sequence_key = 'image/seq_id' + time_horizon = None + reduce_image_size = False + max_dim = None + + rekey_fn = add_context_to_examples.ReKeyDataFn( + sequence_key, time_horizon, + reduce_image_size, max_dim) + output = rekey_fn.process(self._create_first_tf_example()) + + self.assert_expected_key(output[0][0]) + self.assert_size(output[0][1]) + + def test_rekey_data_fn_w_resize(self): + sequence_key = 'image/seq_id' + time_horizon = None + reduce_image_size = True + max_dim = 2 + + rekey_fn = add_context_to_examples.ReKeyDataFn( + sequence_key, time_horizon, + reduce_image_size, max_dim) + output = rekey_fn.process(self._create_first_tf_example()) + + self.assert_expected_key(output[0][0]) + self.assert_resized(output[0][1]) + + def test_sort_fn(self): + sequence_key = 'image/seq_id' + sorted_image_ids = False + max_num_elements_in_context_features = 10 + sort_fn = add_context_to_examples.SortGroupedDataFn( + sequence_key, sorted_image_ids, max_num_elements_in_context_features) + output = sort_fn.process( + ('dummy_key', [tf.train.Example.FromString( + self._create_second_tf_example()), + tf.train.Example.FromString( + self._create_first_tf_example())])) + + self.assert_sorted(output[0][1]) + + def test_add_context_fn(self): + sequence_key = 'image/seq_id' + add_context_features = True + image_ids_to_keep = 'All' + context_fn = add_context_to_examples.GenerateContextFn( + sequence_key, add_context_features, image_ids_to_keep) + output = context_fn.process( + ('dummy_key', [tf.train.Example.FromString( + self._create_first_tf_example()), + tf.train.Example.FromString( + self._create_second_tf_example())])) + + self.assertEqual(len(output), 2) + self.assert_context(output) + + def test_add_context_fn_output_sequence_example(self): + sequence_key = 'image/seq_id' + add_context_features = True + image_ids_to_keep = 'All' + context_fn = add_context_to_examples.GenerateContextFn( + sequence_key, add_context_features, image_ids_to_keep, + output_type='tf_sequence_example') + output = context_fn.process( + ('01', + [tf.train.Example.FromString(self._create_first_tf_example()), + tf.train.Example.FromString(self._create_second_tf_example())])) + + self.assertEqual(len(output), 1) + self.assert_expected_sequence_example(output) + + def test_add_context_fn_output_sequence_example_cliplen(self): + sequence_key = 'image/seq_id' + add_context_features = True + image_ids_to_keep = 'All' + context_fn = add_context_to_examples.GenerateContextFn( + sequence_key, add_context_features, image_ids_to_keep, + output_type='tf_sequence_example', max_clip_length=1) + output = context_fn.process( + ('01', + [tf.train.Example.FromString(self._create_first_tf_example()), + tf.train.Example.FromString(self._create_second_tf_example())])) + self.assertEqual(len(output), 2) + + def test_beam_pipeline(self): + with InMemoryTFRecord( + [self._create_first_tf_example(), + self._create_second_tf_example()]) as input_tfrecord: + temp_dir = tempfile.mkdtemp(dir=os.environ.get('TEST_TMPDIR')) + output_tfrecord = os.path.join(temp_dir, 'output_tfrecord') + sequence_key = six.ensure_binary('image/seq_id') + max_num_elements = 10 + num_shards = 1 + pipeline_options = beam.options.pipeline_options.PipelineOptions( + runner='DirectRunner') + p = beam.Pipeline(options=pipeline_options) + add_context_to_examples.construct_pipeline( + p, + input_tfrecord, + output_tfrecord, + sequence_key, + max_num_elements_in_context_features=max_num_elements, + num_shards=num_shards) + p.run() + filenames = tf.io.gfile.glob(output_tfrecord + '-?????-of-?????') + actual_output = [] + record_iterator = tf.data.TFRecordDataset( + tf.convert_to_tensor(filenames)).as_numpy_iterator() + for record in record_iterator: + actual_output.append(record) + self.assertEqual(len(actual_output), 2) + self.assert_expected_examples([tf.train.Example.FromString( + tf_example) for tf_example in actual_output]) + + def test_beam_pipeline_sequence_example(self): + with InMemoryTFRecord( + [self._create_first_tf_example(), + self._create_second_tf_example()]) as input_tfrecord: + temp_dir = tempfile.mkdtemp(dir=os.environ.get('TEST_TMPDIR')) + output_tfrecord = os.path.join(temp_dir, 'output_tfrecord') + sequence_key = six.ensure_binary('image/seq_id') + max_num_elements = 10 + num_shards = 1 + pipeline_options = beam.options.pipeline_options.PipelineOptions( + runner='DirectRunner') + p = beam.Pipeline(options=pipeline_options) + add_context_to_examples.construct_pipeline( + p, + input_tfrecord, + output_tfrecord, + sequence_key, + max_num_elements_in_context_features=max_num_elements, + num_shards=num_shards, + output_type='tf_sequence_example') + p.run() + filenames = tf.io.gfile.glob(output_tfrecord + '-?????-of-?????') + actual_output = [] + record_iterator = tf.data.TFRecordDataset( + tf.convert_to_tensor(filenames)).as_numpy_iterator() + for record in record_iterator: + actual_output.append(record) + self.assertEqual(len(actual_output), 1) + self.assert_expected_sequence_example( + [tf.train.SequenceExample.FromString( + tf_example) for tf_example in actual_output]) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_main.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_main.py new file mode 100644 index 0000000..dbf3cad --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_main.py @@ -0,0 +1,334 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Beam pipeline to create COCO Camera Traps Object Detection TFRecords. + +Please note that this tool creates sharded output files. + +This tool assumes the input annotations are in the COCO Camera Traps json +format, specified here: +https://github.com/Microsoft/CameraTraps/blob/master/data_management/README.md + +Example usage: + + python create_cococameratraps_tfexample_main.py \ + --alsologtostderr \ + --output_tfrecord_prefix="/path/to/output/tfrecord/location/prefix" \ + --image_directory="/path/to/image/folder/" \ + --input_annotations_file="path/to/annotations.json" + +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import hashlib +import io +import json +import os +import numpy as np +import PIL.Image +import tensorflow as tf +from object_detection.utils import dataset_util + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +class ParseImage(beam.DoFn): + """A DoFn that parses a COCO-CameraTraps json and emits TFRecords.""" + + def __init__(self, image_directory, images, annotations, categories, + keep_bboxes): + """Initialization function. + + Args: + image_directory: Path to image directory + images: list of COCO Camera Traps style image dictionaries + annotations: list of COCO Camera Traps style annotation dictionaries + categories: list of COCO Camera Traps style category dictionaries + keep_bboxes: Whether to keep any bounding boxes that exist in the + annotations + """ + + self._image_directory = image_directory + self._image_dict = {im['id']: im for im in images} + self._annotation_dict = {im['id']: [] for im in images} + self._category_dict = {int(cat['id']): cat for cat in categories} + for ann in annotations: + self._annotation_dict[ann['image_id']].append(ann) + self._images = images + self._keep_bboxes = keep_bboxes + + self._num_examples_processed = beam.metrics.Metrics.counter( + 'cococameratraps_data_generation', 'num_tf_examples_processed') + + def process(self, image_id): + """Builds a tf.Example given an image id. + + Args: + image_id: the image id of the associated image + + Returns: + List of tf.Examples. + """ + + image = self._image_dict[image_id] + annotations = self._annotation_dict[image_id] + image_height = image['height'] + image_width = image['width'] + filename = image['file_name'] + image_id = image['id'] + image_location_id = image['location'] + + image_datetime = str(image['date_captured']) + + image_sequence_id = str(image['seq_id']) + image_sequence_num_frames = int(image['seq_num_frames']) + image_sequence_frame_num = int(image['frame_num']) + + full_path = os.path.join(self._image_directory, filename) + + try: + # Ensure the image exists and is not corrupted + with tf.io.gfile.GFile(full_path, 'rb') as fid: + encoded_jpg = fid.read() + encoded_jpg_io = io.BytesIO(encoded_jpg) + image = PIL.Image.open(encoded_jpg_io) + image = tf.io.decode_jpeg(encoded_jpg, channels=3) + except Exception: # pylint: disable=broad-except + # The image file is missing or corrupt + return [] + + key = hashlib.sha256(encoded_jpg).hexdigest() + feature_dict = { + 'image/height': + dataset_util.int64_feature(image_height), + 'image/width': + dataset_util.int64_feature(image_width), + 'image/filename': + dataset_util.bytes_feature(filename.encode('utf8')), + 'image/source_id': + dataset_util.bytes_feature(str(image_id).encode('utf8')), + 'image/key/sha256': + dataset_util.bytes_feature(key.encode('utf8')), + 'image/encoded': + dataset_util.bytes_feature(encoded_jpg), + 'image/format': + dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/location': + dataset_util.bytes_feature(str(image_location_id).encode('utf8')), + 'image/seq_num_frames': + dataset_util.int64_feature(image_sequence_num_frames), + 'image/seq_frame_num': + dataset_util.int64_feature(image_sequence_frame_num), + 'image/seq_id': + dataset_util.bytes_feature(image_sequence_id.encode('utf8')), + 'image/date_captured': + dataset_util.bytes_feature(image_datetime.encode('utf8')) + } + + num_annotations_skipped = 0 + if annotations: + xmin = [] + xmax = [] + ymin = [] + ymax = [] + category_names = [] + category_ids = [] + area = [] + + for object_annotations in annotations: + if 'bbox' in object_annotations and self._keep_bboxes: + (x, y, width, height) = tuple(object_annotations['bbox']) + if width <= 0 or height <= 0: + num_annotations_skipped += 1 + continue + if x + width > image_width or y + height > image_height: + num_annotations_skipped += 1 + continue + xmin.append(float(x) / image_width) + xmax.append(float(x + width) / image_width) + ymin.append(float(y) / image_height) + ymax.append(float(y + height) / image_height) + if 'area' in object_annotations: + area.append(object_annotations['area']) + else: + # approximate area using l*w/2 + area.append(width*height/2.0) + + category_id = int(object_annotations['category_id']) + category_ids.append(category_id) + category_names.append( + self._category_dict[category_id]['name'].encode('utf8')) + + feature_dict.update({ + 'image/object/bbox/xmin': + dataset_util.float_list_feature(xmin), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(xmax), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(ymin), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(ymax), + 'image/object/class/text': + dataset_util.bytes_list_feature(category_names), + 'image/object/class/label': + dataset_util.int64_list_feature(category_ids), + 'image/object/area': + dataset_util.float_list_feature(area), + }) + + # For classification, add the first category to image/class/label and + # image/class/text + if not category_ids: + feature_dict.update({ + 'image/class/label': + dataset_util.int64_list_feature([0]), + 'image/class/text': + dataset_util.bytes_list_feature(['empty'.encode('utf8')]), + }) + else: + feature_dict.update({ + 'image/class/label': + dataset_util.int64_list_feature([category_ids[0]]), + 'image/class/text': + dataset_util.bytes_list_feature([category_names[0]]), + }) + + else: + # Add empty class if there are no annotations + feature_dict.update({ + 'image/class/label': + dataset_util.int64_list_feature([0]), + 'image/class/text': + dataset_util.bytes_list_feature(['empty'.encode('utf8')]), + }) + + example = tf.train.Example(features=tf.train.Features(feature=feature_dict)) + self._num_examples_processed.inc(1) + + return [(example)] + + +def load_json_data(data_file): + with tf.io.gfile.GFile(data_file, 'r') as fid: + data_dict = json.load(fid) + return data_dict + + +def create_pipeline(pipeline, + image_directory, + input_annotations_file, + output_tfrecord_prefix=None, + num_images_per_shard=200, + keep_bboxes=True): + """Creates a beam pipeline for producing a COCO-CameraTraps Image dataset. + + Args: + pipeline: Initialized beam pipeline. + image_directory: Path to image directory + input_annotations_file: Path to a coco-cameratraps annotation file + output_tfrecord_prefix: Absolute path for tfrecord outputs. Final files will + be named {output_tfrecord_prefix}@N. + num_images_per_shard: The number of images to store in each shard + keep_bboxes: Whether to keep any bounding boxes that exist in the json file + """ + + data = load_json_data(input_annotations_file) + + num_shards = int(np.ceil(float(len(data['images']))/num_images_per_shard)) + + image_examples = ( + pipeline | ('CreateCollections') >> beam.Create( + [im['id'] for im in data['images']]) + | ('ParseImage') >> beam.ParDo(ParseImage( + image_directory, data['images'], data['annotations'], + data['categories'], keep_bboxes=keep_bboxes))) + _ = (image_examples + | ('Reshuffle') >> beam.Reshuffle() + | ('WriteTfImageExample') >> beam.io.tfrecordio.WriteToTFRecord( + output_tfrecord_prefix, + num_shards=num_shards, + coder=beam.coders.ProtoCoder(tf.train.Example))) + + +def parse_args(argv): + """Command-line argument parser. + + Args: + argv: command line arguments + Returns: + beam_args: Arguments for the beam pipeline. + pipeline_args: Arguments for the pipeline options, such as runner type. + """ + parser = argparse.ArgumentParser() + parser.add_argument( + '--image_directory', + dest='image_directory', + required=True, + help='Path to the directory where the images are stored.') + parser.add_argument( + '--output_tfrecord_prefix', + dest='output_tfrecord_prefix', + required=True, + help='Path and prefix to store TFRecords containing images in tf.Example' + 'format.') + parser.add_argument( + '--input_annotations_file', + dest='input_annotations_file', + required=True, + help='Path to Coco-CameraTraps style annotations file.') + parser.add_argument( + '--num_images_per_shard', + dest='num_images_per_shard', + default=200, + help='The number of images to be stored in each outputshard.') + beam_args, pipeline_args = parser.parse_known_args(argv) + return beam_args, pipeline_args + + +def main(argv=None, save_main_session=True): + """Runs the Beam pipeline that performs inference. + + Args: + argv: Command line arguments. + save_main_session: Whether to save the main session. + """ + args, pipeline_args = parse_args(argv) + + pipeline_options = beam.options.pipeline_options.PipelineOptions( + pipeline_args) + pipeline_options.view_as( + beam.options.pipeline_options.SetupOptions).save_main_session = ( + save_main_session) + + dirname = os.path.dirname(args.output_tfrecord_prefix) + tf.io.gfile.makedirs(dirname) + + p = beam.Pipeline(options=pipeline_options) + create_pipeline( + pipeline=p, + image_directory=args.image_directory, + input_annotations_file=args.input_annotations_file, + output_tfrecord_prefix=args.output_tfrecord_prefix, + num_images_per_shard=args.num_images_per_shard) + p.run() + + +if __name__ == '__main__': + main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_tf2_test.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_tf2_test.py new file mode 100644 index 0000000..0a1ac20 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_tf2_test.py @@ -0,0 +1,214 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for create_cococameratraps_tfexample_main.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import datetime +import json +import os +import tempfile +import unittest + +import numpy as np + +from PIL import Image +import tensorflow as tf +from object_detection.utils import tf_version + +if tf_version.is_tf2(): + from object_detection.dataset_tools.context_rcnn import create_cococameratraps_tfexample_main # pylint:disable=g-import-not-at-top + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class CreateCOCOCameraTrapsTfexampleTest(tf.test.TestCase): + + IMAGE_HEIGHT = 360 + IMAGE_WIDTH = 480 + + def _write_random_images_to_directory(self, directory, num_frames): + for frame_num in range(num_frames): + img = np.random.randint(0, high=256, + size=(self.IMAGE_HEIGHT, self.IMAGE_WIDTH, 3), + dtype=np.uint8) + pil_image = Image.fromarray(img) + fname = 'im_' + str(frame_num) + '.jpg' + pil_image.save(os.path.join(directory, fname), 'JPEG') + + def _create_json_file(self, directory, num_frames, keep_bboxes=False): + json_dict = {'images': [], 'annotations': []} + json_dict['categories'] = [{'id': 0, 'name': 'empty'}, + {'id': 1, 'name': 'animal'}] + for idx in range(num_frames): + im = {'id': 'im_' + str(idx), + 'file_name': 'im_' + str(idx) + '.jpg', + 'height': self.IMAGE_HEIGHT, + 'width': self.IMAGE_WIDTH, + 'seq_id': 'seq_1', + 'seq_num_frames': num_frames, + 'frame_num': idx, + 'location': 'loc_' + str(idx), + 'date_captured': str(datetime.datetime.now()) + } + json_dict['images'].append(im) + ann = {'id': 'ann' + str(idx), + 'image_id': 'im_' + str(idx), + 'category_id': 1, + } + if keep_bboxes: + ann['bbox'] = [0.0 * self.IMAGE_WIDTH, + 0.1 * self.IMAGE_HEIGHT, + 0.5 * self.IMAGE_WIDTH, + 0.5 * self.IMAGE_HEIGHT] + json_dict['annotations'].append(ann) + + json_path = os.path.join(directory, 'test_file.json') + with tf.io.gfile.GFile(json_path, 'w') as f: + json.dump(json_dict, f) + return json_path + + def assert_expected_example_bbox(self, example): + self.assertAllClose( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.1]) + self.assertAllClose( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.0]) + self.assertAllClose( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.6]) + self.assertAllClose( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.5]) + self.assertAllClose( + example.features.feature['image/object/class/label'] + .int64_list.value, [1]) + self.assertAllEqual( + example.features.feature['image/object/class/text'] + .bytes_list.value, [b'animal']) + self.assertAllClose( + example.features.feature['image/class/label'] + .int64_list.value, [1]) + self.assertAllEqual( + example.features.feature['image/class/text'] + .bytes_list.value, [b'animal']) + + # Check other essential attributes. + self.assertAllEqual( + example.features.feature['image/height'].int64_list.value, + [self.IMAGE_HEIGHT]) + self.assertAllEqual( + example.features.feature['image/width'].int64_list.value, + [self.IMAGE_WIDTH]) + self.assertAllEqual( + example.features.feature['image/source_id'].bytes_list.value, + [b'im_0']) + self.assertTrue( + example.features.feature['image/encoded'].bytes_list.value) + + def assert_expected_example(self, example): + self.assertAllClose( + example.features.feature['image/object/bbox/ymin'].float_list.value, + []) + self.assertAllClose( + example.features.feature['image/object/bbox/xmin'].float_list.value, + []) + self.assertAllClose( + example.features.feature['image/object/bbox/ymax'].float_list.value, + []) + self.assertAllClose( + example.features.feature['image/object/bbox/xmax'].float_list.value, + []) + self.assertAllClose( + example.features.feature['image/object/class/label'] + .int64_list.value, [1]) + self.assertAllEqual( + example.features.feature['image/object/class/text'] + .bytes_list.value, [b'animal']) + self.assertAllClose( + example.features.feature['image/class/label'] + .int64_list.value, [1]) + self.assertAllEqual( + example.features.feature['image/class/text'] + .bytes_list.value, [b'animal']) + + # Check other essential attributes. + self.assertAllEqual( + example.features.feature['image/height'].int64_list.value, + [self.IMAGE_HEIGHT]) + self.assertAllEqual( + example.features.feature['image/width'].int64_list.value, + [self.IMAGE_WIDTH]) + self.assertAllEqual( + example.features.feature['image/source_id'].bytes_list.value, + [b'im_0']) + self.assertTrue( + example.features.feature['image/encoded'].bytes_list.value) + + def test_beam_pipeline(self): + num_frames = 1 + temp_dir = tempfile.mkdtemp(dir=os.environ.get('TEST_TMPDIR')) + json_path = self._create_json_file(temp_dir, num_frames) + output_tfrecord = temp_dir+'/output' + self._write_random_images_to_directory(temp_dir, num_frames) + pipeline_options = beam.options.pipeline_options.PipelineOptions( + runner='DirectRunner') + p = beam.Pipeline(options=pipeline_options) + create_cococameratraps_tfexample_main.create_pipeline( + p, temp_dir, json_path, + output_tfrecord_prefix=output_tfrecord) + p.run() + filenames = tf.io.gfile.glob(output_tfrecord + '-?????-of-?????') + actual_output = [] + record_iterator = tf.data.TFRecordDataset( + tf.convert_to_tensor(filenames)).as_numpy_iterator() + for record in record_iterator: + actual_output.append(record) + self.assertEqual(len(actual_output), num_frames) + self.assert_expected_example(tf.train.Example.FromString( + actual_output[0])) + + def test_beam_pipeline_bbox(self): + num_frames = 1 + temp_dir = tempfile.mkdtemp(dir=os.environ.get('TEST_TMPDIR')) + json_path = self._create_json_file(temp_dir, num_frames, keep_bboxes=True) + output_tfrecord = temp_dir+'/output' + self._write_random_images_to_directory(temp_dir, num_frames) + pipeline_options = beam.options.pipeline_options.PipelineOptions( + runner='DirectRunner') + p = beam.Pipeline(options=pipeline_options) + create_cococameratraps_tfexample_main.create_pipeline( + p, temp_dir, json_path, + output_tfrecord_prefix=output_tfrecord, + keep_bboxes=True) + p.run() + filenames = tf.io.gfile.glob(output_tfrecord+'-?????-of-?????') + actual_output = [] + record_iterator = tf.data.TFRecordDataset( + tf.convert_to_tensor(filenames)).as_numpy_iterator() + for record in record_iterator: + actual_output.append(record) + self.assertEqual(len(actual_output), num_frames) + self.assert_expected_example_bbox(tf.train.Example.FromString( + actual_output[0])) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data.py new file mode 100644 index 0000000..c826873 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data.py @@ -0,0 +1,283 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""A Beam job to generate detection data for camera trap images. + +This tools allows to run inference with an exported Object Detection model in +`saved_model` format and produce raw detection boxes on images in tf.Examples, +with the assumption that the bounding box class label will match the image-level +class label in the tf.Example. + +Steps to generate a detection dataset: +1. Use object_detection/export_inference_graph.py to get a `saved_model` for + inference. The input node must accept a tf.Example proto. +2. Run this tool with `saved_model` from step 1 and an TFRecord of tf.Example + protos containing images for inference. + +Example Usage: +-------------- +python tensorflow_models/object_detection/export_inference_graph.py \ + --alsologtostderr \ + --input_type tf_example \ + --pipeline_config_path path/to/detection_model.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory + +python generate_detection_data.py \ + --alsologtostderr \ + --input_tfrecord path/to/input_tfrecord@X \ + --output_tfrecord path/to/output_tfrecord@X \ + --model_dir path/to/exported_model_directory/saved_model +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import os +import threading +import tensorflow as tf + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +class GenerateDetectionDataFn(beam.DoFn): + """Generates detection data for camera trap images. + + This Beam DoFn performs inference with an object detection `saved_model` and + produces detection boxes for camera trap data, matched to the + object class. + """ + session_lock = threading.Lock() + + def __init__(self, model_dir, confidence_threshold): + """Initialization function. + + Args: + model_dir: A directory containing saved model. + confidence_threshold: the confidence threshold for boxes to keep + """ + self._model_dir = model_dir + self._confidence_threshold = confidence_threshold + self._session = None + self._num_examples_processed = beam.metrics.Metrics.counter( + 'detection_data_generation', 'num_tf_examples_processed') + + def setup(self): + self._load_inference_model() + + def _load_inference_model(self): + # Because initialization of the tf.Session is expensive we share + # one instance across all threads in the worker. This is possible since + # tf.Session.run() is thread safe. + with self.session_lock: + self._detect_fn = tf.saved_model.load(self._model_dir) + + def process(self, tfrecord_entry): + return self._run_inference_and_generate_detections(tfrecord_entry) + + def _run_inference_and_generate_detections(self, tfrecord_entry): + input_example = tf.train.Example.FromString(tfrecord_entry) + if input_example.features.feature[ + 'image/object/bbox/ymin'].float_list.value: + # There are already ground truth boxes for this image, just keep them. + return [input_example] + + detections = self._detect_fn.signatures['serving_default']( + (tf.expand_dims(tf.convert_to_tensor(tfrecord_entry), 0))) + detection_boxes = detections['detection_boxes'] + num_detections = detections['num_detections'] + detection_scores = detections['detection_scores'] + + example = tf.train.Example() + + num_detections = int(num_detections[0]) + + image_class_labels = input_example.features.feature[ + 'image/object/class/label'].int64_list.value + + image_class_texts = input_example.features.feature[ + 'image/object/class/text'].bytes_list.value + + # Ignore any images with multiple classes, + # we can't match the class to the box. + if len(image_class_labels) > 1: + return [] + + # Don't add boxes for images already labeled empty (for now) + if len(image_class_labels) == 1: + # Add boxes over confidence threshold. + for idx, score in enumerate(detection_scores[0]): + if score >= self._confidence_threshold and idx < num_detections: + example.features.feature[ + 'image/object/bbox/ymin'].float_list.value.extend([ + detection_boxes[0, idx, 0]]) + example.features.feature[ + 'image/object/bbox/xmin'].float_list.value.extend([ + detection_boxes[0, idx, 1]]) + example.features.feature[ + 'image/object/bbox/ymax'].float_list.value.extend([ + detection_boxes[0, idx, 2]]) + example.features.feature[ + 'image/object/bbox/xmax'].float_list.value.extend([ + detection_boxes[0, idx, 3]]) + + # Add box scores and class texts and labels. + example.features.feature[ + 'image/object/class/score'].float_list.value.extend( + [score]) + + example.features.feature[ + 'image/object/class/label'].int64_list.value.extend( + [image_class_labels[0]]) + + example.features.feature[ + 'image/object/class/text'].bytes_list.value.extend( + [image_class_texts[0]]) + + # Add other essential example attributes + example.features.feature['image/encoded'].bytes_list.value.extend( + input_example.features.feature['image/encoded'].bytes_list.value) + example.features.feature['image/height'].int64_list.value.extend( + input_example.features.feature['image/height'].int64_list.value) + example.features.feature['image/width'].int64_list.value.extend( + input_example.features.feature['image/width'].int64_list.value) + example.features.feature['image/source_id'].bytes_list.value.extend( + input_example.features.feature['image/source_id'].bytes_list.value) + example.features.feature['image/location'].bytes_list.value.extend( + input_example.features.feature['image/location'].bytes_list.value) + + example.features.feature['image/date_captured'].bytes_list.value.extend( + input_example.features.feature['image/date_captured'].bytes_list.value) + + example.features.feature['image/class/text'].bytes_list.value.extend( + input_example.features.feature['image/class/text'].bytes_list.value) + example.features.feature['image/class/label'].int64_list.value.extend( + input_example.features.feature['image/class/label'].int64_list.value) + + example.features.feature['image/seq_id'].bytes_list.value.extend( + input_example.features.feature['image/seq_id'].bytes_list.value) + example.features.feature['image/seq_num_frames'].int64_list.value.extend( + input_example.features.feature['image/seq_num_frames'].int64_list.value) + example.features.feature['image/seq_frame_num'].int64_list.value.extend( + input_example.features.feature['image/seq_frame_num'].int64_list.value) + + self._num_examples_processed.inc(1) + return [example] + + +def construct_pipeline(pipeline, input_tfrecord, output_tfrecord, model_dir, + confidence_threshold, num_shards): + """Returns a Beam pipeline to run object detection inference. + + Args: + pipeline: Initialized beam pipeline. + input_tfrecord: A TFRecord of tf.train.Example protos containing images. + output_tfrecord: A TFRecord of tf.train.Example protos that contain images + in the input TFRecord and the detections from the model. + model_dir: Path to `saved_model` to use for inference. + confidence_threshold: Threshold to use when keeping detection results. + num_shards: The number of output shards. + """ + input_collection = ( + pipeline | 'ReadInputTFRecord' >> beam.io.tfrecordio.ReadFromTFRecord( + input_tfrecord, + coder=beam.coders.BytesCoder())) + output_collection = input_collection | 'RunInference' >> beam.ParDo( + GenerateDetectionDataFn(model_dir, confidence_threshold)) + output_collection = output_collection | 'Reshuffle' >> beam.Reshuffle() + _ = output_collection | 'WritetoDisk' >> beam.io.tfrecordio.WriteToTFRecord( + output_tfrecord, + num_shards=num_shards, + coder=beam.coders.ProtoCoder(tf.train.Example)) + + +def parse_args(argv): + """Command-line argument parser. + + Args: + argv: command line arguments + Returns: + beam_args: Arguments for the beam pipeline. + pipeline_args: Arguments for the pipeline options, such as runner type. + """ + parser = argparse.ArgumentParser() + parser.add_argument( + '--detection_input_tfrecord', + dest='detection_input_tfrecord', + required=True, + help='TFRecord containing images in tf.Example format for object ' + 'detection.') + parser.add_argument( + '--detection_output_tfrecord', + dest='detection_output_tfrecord', + required=True, + help='TFRecord containing detections in tf.Example format.') + parser.add_argument( + '--detection_model_dir', + dest='detection_model_dir', + required=True, + help='Path to directory containing an object detection SavedModel.') + parser.add_argument( + '--confidence_threshold', + dest='confidence_threshold', + default=0.9, + help='Min confidence to keep bounding boxes.') + parser.add_argument( + '--num_shards', + dest='num_shards', + default=0, + help='Number of output shards.') + beam_args, pipeline_args = parser.parse_known_args(argv) + return beam_args, pipeline_args + + +def main(argv=None, save_main_session=True): + """Runs the Beam pipeline that performs inference. + + Args: + argv: Command line arguments. + save_main_session: Whether to save the main session. + """ + + args, pipeline_args = parse_args(argv) + + pipeline_options = beam.options.pipeline_options.PipelineOptions( + pipeline_args) + pipeline_options.view_as( + beam.options.pipeline_options.SetupOptions).save_main_session = ( + save_main_session) + + dirname = os.path.dirname(args.detection_output_tfrecord) + tf.io.gfile.makedirs(dirname) + + p = beam.Pipeline(options=pipeline_options) + + construct_pipeline( + p, + args.detection_input_tfrecord, + args.detection_output_tfrecord, + args.detection_model_dir, + args.confidence_threshold, + args.num_shards) + + p.run() + + +if __name__ == '__main__': + main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data_tf2_test.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data_tf2_test.py new file mode 100644 index 0000000..71b3276 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_detection_data_tf2_test.py @@ -0,0 +1,261 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for generate_detection_data.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import contextlib +import os +import tempfile +import unittest +import numpy as np +import six +import tensorflow as tf + +from object_detection import exporter_lib_v2 +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.protos import pipeline_pb2 +from object_detection.utils import tf_version + +if tf_version.is_tf2(): + from object_detection.dataset_tools.context_rcnn import generate_detection_data # pylint:disable=g-import-not-at-top + +if six.PY2: + import mock # pylint: disable=g-import-not-at-top +else: + mock = unittest.mock + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +class FakeModel(model.DetectionModel): + + def __init__(self, conv_weight_scalar=1.0): + super(FakeModel, self).__init__(num_classes=5) + self._conv = tf.keras.layers.Conv2D( + filters=1, kernel_size=1, strides=(1, 1), padding='valid', + kernel_initializer=tf.keras.initializers.Constant( + value=conv_weight_scalar)) + + def preprocess(self, inputs): + true_image_shapes = [] # Doesn't matter for the fake model. + return tf.identity(inputs), true_image_shapes + + def predict(self, preprocessed_inputs, true_image_shapes): + return {'image': self._conv(preprocessed_inputs)} + + def postprocess(self, prediction_dict, true_image_shapes): + with tf.control_dependencies(list(prediction_dict.values())): + postprocessed_tensors = { + 'detection_boxes': tf.constant([[[0.0, 0.1, 0.5, 0.6], + [0.5, 0.5, 0.8, 0.8]]], tf.float32), + 'detection_scores': tf.constant([[0.95, 0.6]], tf.float32), + 'detection_multiclass_scores': tf.constant([[[0.1, 0.7, 0.2], + [0.3, 0.1, 0.6]]], + tf.float32), + 'detection_classes': tf.constant([[0, 1]], tf.float32), + 'num_detections': tf.constant([2], tf.float32) + } + return postprocessed_tensors + + def restore_map(self, checkpoint_path, fine_tune_checkpoint_type): + pass + + def restore_from_objects(self, fine_tune_checkpoint_type): + pass + + def loss(self, prediction_dict, true_image_shapes): + pass + + def regularization_losses(self): + pass + + def updates(self): + pass + + +@contextlib.contextmanager +def InMemoryTFRecord(entries): + temp = tempfile.NamedTemporaryFile(delete=False) + filename = temp.name + try: + with tf.io.TFRecordWriter(filename) as writer: + for value in entries: + writer.write(value) + yield filename + finally: + os.unlink(filename) + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class GenerateDetectionDataTest(tf.test.TestCase): + + def _save_checkpoint_from_mock_model(self, checkpoint_path): + """A function to save checkpoint from a fake Detection Model. + + Args: + checkpoint_path: Path to save checkpoint from Fake model. + """ + mock_model = FakeModel() + fake_image = tf.zeros(shape=[1, 10, 10, 3], dtype=tf.float32) + preprocessed_inputs, true_image_shapes = mock_model.preprocess(fake_image) + predictions = mock_model.predict(preprocessed_inputs, true_image_shapes) + mock_model.postprocess(predictions, true_image_shapes) + ckpt = tf.train.Checkpoint(model=mock_model) + exported_checkpoint_manager = tf.train.CheckpointManager( + ckpt, checkpoint_path, max_to_keep=1) + exported_checkpoint_manager.save(checkpoint_number=0) + + def _export_saved_model(self): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + output_directory = os.path.join(tmp_dir, 'output') + saved_model_path = os.path.join(output_directory, 'saved_model') + tf.io.gfile.makedirs(output_directory) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + exporter_lib_v2.INPUT_BUILDER_UTIL_MAP['model_build'] = mock_builder + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter_lib_v2.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory) + saved_model_path = os.path.join(output_directory, 'saved_model') + return saved_model_path + + def _create_tf_example(self): + with self.test_session(): + encoded_image = tf.io.encode_jpeg( + tf.constant(np.ones((4, 6, 3)).astype(np.uint8))).numpy() + + def BytesFeature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) + + def Int64Feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': BytesFeature(encoded_image), + 'image/source_id': BytesFeature(b'image_id'), + 'image/height': Int64Feature(4), + 'image/width': Int64Feature(6), + 'image/object/class/label': Int64Feature(5), + 'image/object/class/text': BytesFeature(b'hyena'), + 'image/class/label': Int64Feature(5), + 'image/class/text': BytesFeature(b'hyena'), + })) + + return example.SerializeToString() + + def assert_expected_example(self, example): + self.assertAllClose( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.0]) + self.assertAllClose( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.1]) + self.assertAllClose( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.5]) + self.assertAllClose( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.6]) + self.assertAllClose( + example.features.feature['image/object/class/score'] + .float_list.value, [0.95]) + self.assertAllClose( + example.features.feature['image/object/class/label'] + .int64_list.value, [5]) + self.assertAllEqual( + example.features.feature['image/object/class/text'] + .bytes_list.value, [b'hyena']) + self.assertAllClose( + example.features.feature['image/class/label'] + .int64_list.value, [5]) + self.assertAllEqual( + example.features.feature['image/class/text'] + .bytes_list.value, [b'hyena']) + + # Check other essential attributes. + self.assertAllEqual( + example.features.feature['image/height'].int64_list.value, [4]) + self.assertAllEqual( + example.features.feature['image/width'].int64_list.value, [6]) + self.assertAllEqual( + example.features.feature['image/source_id'].bytes_list.value, + [b'image_id']) + self.assertTrue( + example.features.feature['image/encoded'].bytes_list.value) + + def test_generate_detection_data_fn(self): + saved_model_path = self._export_saved_model() + confidence_threshold = 0.8 + inference_fn = generate_detection_data.GenerateDetectionDataFn( + saved_model_path, confidence_threshold) + inference_fn.setup() + generated_example = self._create_tf_example() + self.assertAllEqual(tf.train.Example.FromString( + generated_example).features.feature['image/object/class/label'] + .int64_list.value, [5]) + self.assertAllEqual(tf.train.Example.FromString( + generated_example).features.feature['image/object/class/text'] + .bytes_list.value, [b'hyena']) + output = inference_fn.process(generated_example) + output_example = output[0] + + self.assertAllEqual( + output_example.features.feature['image/object/class/label'] + .int64_list.value, [5]) + self.assertAllEqual(output_example.features.feature['image/width'] + .int64_list.value, [6]) + + self.assert_expected_example(output_example) + + def test_beam_pipeline(self): + with InMemoryTFRecord([self._create_tf_example()]) as input_tfrecord: + temp_dir = tempfile.mkdtemp(dir=os.environ.get('TEST_TMPDIR')) + output_tfrecord = os.path.join(temp_dir, 'output_tfrecord') + saved_model_path = self._export_saved_model() + confidence_threshold = 0.8 + num_shards = 1 + pipeline_options = beam.options.pipeline_options.PipelineOptions( + runner='DirectRunner') + p = beam.Pipeline(options=pipeline_options) + generate_detection_data.construct_pipeline( + p, input_tfrecord, output_tfrecord, saved_model_path, + confidence_threshold, num_shards) + p.run() + filenames = tf.io.gfile.glob(output_tfrecord + '-?????-of-?????') + actual_output = [] + record_iterator = tf.data.TFRecordDataset( + tf.convert_to_tensor(filenames)).as_numpy_iterator() + for record in record_iterator: + actual_output.append(record) + self.assertEqual(len(actual_output), 1) + self.assert_expected_example(tf.train.Example.FromString( + actual_output[0])) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data.py new file mode 100644 index 0000000..02e1382 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data.py @@ -0,0 +1,355 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""A Beam job to generate embedding data for camera trap images. + +This tool runs inference with an exported Object Detection model in +`saved_model` format and produce raw embeddings for camera trap data. These +embeddings contain an object-centric feature embedding from Faster R-CNN, the +datetime that the image was taken (normalized in a specific way), and the +position of the object of interest. By default, only the highest-scoring object +embedding is included. + +Steps to generate a embedding dataset: +1. Use object_detection/export_inference_graph.py to get a Faster R-CNN + `saved_model` for inference. The input node must accept a tf.Example proto. +2. Run this tool with `saved_model` from step 1 and an TFRecord of tf.Example + protos containing images for inference. + +Example Usage: +-------------- +python tensorflow_models/object_detection/export_inference_graph.py \ + --alsologtostderr \ + --input_type tf_example \ + --pipeline_config_path path/to/faster_rcnn_model.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory \ + --additional_output_tensor_names detection_features + +python generate_embedding_data.py \ + --alsologtostderr \ + --embedding_input_tfrecord path/to/input_tfrecords* \ + --embedding_output_tfrecord path/to/output_tfrecords \ + --embedding_model_dir path/to/exported_model_directory/saved_model +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import argparse +import datetime +import os +import threading + +import numpy as np +import six +import tensorflow as tf + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +def add_keys(serialized_example): + key = hash(serialized_example) + return key, serialized_example + + +def drop_keys(key_value_tuple): + return key_value_tuple[1] + + +def get_date_captured(example): + date_captured = datetime.datetime.strptime( + six.ensure_str( + example.features.feature['image/date_captured'].bytes_list.value[0]), + '%Y-%m-%d %H:%M:%S') + return date_captured + + +def embed_date_captured(date_captured): + """Encodes the datetime of the image.""" + embedded_date_captured = [] + month_max = 12.0 + day_max = 31.0 + hour_max = 24.0 + minute_max = 60.0 + min_year = 1990.0 + max_year = 2030.0 + + year = (date_captured.year - min_year) / float(max_year - min_year) + embedded_date_captured.append(year) + + month = (date_captured.month - 1) / month_max + embedded_date_captured.append(month) + + day = (date_captured.day - 1) / day_max + embedded_date_captured.append(day) + + hour = date_captured.hour / hour_max + embedded_date_captured.append(hour) + + minute = date_captured.minute / minute_max + embedded_date_captured.append(minute) + + return np.asarray(embedded_date_captured) + + +def embed_position_and_size(box): + """Encodes the bounding box of the object of interest.""" + ymin = box[0] + xmin = box[1] + ymax = box[2] + xmax = box[3] + w = xmax - xmin + h = ymax - ymin + x = xmin + w / 2.0 + y = ymin + h / 2.0 + return np.asarray([x, y, w, h]) + + +def get_bb_embedding(detection_features, detection_boxes, detection_scores, + index): + embedding = detection_features[0][index] + pooled_embedding = np.mean(np.mean(embedding, axis=1), axis=0) + + box = detection_boxes[0][index] + position_embedding = embed_position_and_size(box) + + score = detection_scores[0][index] + return np.concatenate((pooled_embedding, position_embedding)), score + + +class GenerateEmbeddingDataFn(beam.DoFn): + """Generates embedding data for camera trap images. + + This Beam DoFn performs inference with an object detection `saved_model` and + produces contextual embedding vectors. + """ + session_lock = threading.Lock() + + def __init__(self, model_dir, top_k_embedding_count, + bottom_k_embedding_count): + """Initialization function. + + Args: + model_dir: A directory containing saved model. + top_k_embedding_count: the number of high-confidence embeddings to store + bottom_k_embedding_count: the number of low-confidence embeddings to store + """ + self._model_dir = model_dir + self._session = None + self._num_examples_processed = beam.metrics.Metrics.counter( + 'embedding_data_generation', 'num_tf_examples_processed') + self._top_k_embedding_count = top_k_embedding_count + self._bottom_k_embedding_count = bottom_k_embedding_count + + def setup(self): + self._load_inference_model() + + def _load_inference_model(self): + # Because initialization of the tf.Session is expensive we share + # one instance across all threads in the worker. This is possible since + # tf.Session.run() is thread safe. + with self.session_lock: + self._detect_fn = tf.saved_model.load(self._model_dir) + + def process(self, tfexample_key_value): + return self._run_inference_and_generate_embedding(tfexample_key_value) + + def _run_inference_and_generate_embedding(self, tfexample_key_value): + key, tfexample = tfexample_key_value + input_example = tf.train.Example.FromString(tfexample) + example = tf.train.Example() + example.CopyFrom(input_example) + + try: + date_captured = get_date_captured(input_example) + unix_time = ((date_captured - + datetime.datetime.fromtimestamp(0)).total_seconds()) + example.features.feature['image/unix_time'].float_list.value.extend( + [unix_time]) + temporal_embedding = embed_date_captured(date_captured) + except Exception: # pylint: disable=broad-except + temporal_embedding = None + + detections = self._detect_fn.signatures['serving_default']( + (tf.expand_dims(tf.convert_to_tensor(tfexample), 0))) + detection_features = detections['detection_features'] + detection_boxes = detections['detection_boxes'] + num_detections = detections['num_detections'] + detection_scores = detections['detection_scores'] + + num_detections = int(num_detections) + embed_all = [] + score_all = [] + + detection_features = np.asarray(detection_features) + + embedding_count = 0 + for index in range(min(num_detections, self._top_k_embedding_count)): + bb_embedding, score = get_bb_embedding( + detection_features, detection_boxes, detection_scores, index) + embed_all.extend(bb_embedding) + if temporal_embedding is not None: embed_all.extend(temporal_embedding) + score_all.append(score) + embedding_count += 1 + + for index in range( + max(0, num_detections - 1), + max(-1, num_detections - 1 - self._bottom_k_embedding_count), -1): + bb_embedding, score = get_bb_embedding( + detection_features, detection_boxes, detection_scores, index) + embed_all.extend(bb_embedding) + if temporal_embedding is not None: embed_all.extend(temporal_embedding) + score_all.append(score) + embedding_count += 1 + + if embedding_count == 0: + bb_embedding, score = get_bb_embedding( + detection_features, detection_boxes, detection_scores, 0) + embed_all.extend(bb_embedding) + if temporal_embedding is not None: embed_all.extend(temporal_embedding) + score_all.append(score) + + # Takes max in case embedding_count is 0. + embedding_length = len(embed_all) // max(1, embedding_count) + + embed_all = np.asarray(embed_all) + + example.features.feature['image/embedding'].float_list.value.extend( + embed_all) + example.features.feature['image/embedding_score'].float_list.value.extend( + score_all) + example.features.feature['image/embedding_length'].int64_list.value.append( + embedding_length) + example.features.feature['image/embedding_count'].int64_list.value.append( + embedding_count) + + self._num_examples_processed.inc(1) + return [(key, example)] + + +def construct_pipeline(pipeline, input_tfrecord, output_tfrecord, model_dir, + top_k_embedding_count, bottom_k_embedding_count, + num_shards): + """Returns a beam pipeline to run object detection inference. + + Args: + pipeline: Initialized beam pipeline. + input_tfrecord: An TFRecord of tf.train.Example protos containing images. + output_tfrecord: An TFRecord of tf.train.Example protos that contain images + in the input TFRecord and the detections from the model. + model_dir: Path to `saved_model` to use for inference. + top_k_embedding_count: The number of high-confidence embeddings to store. + bottom_k_embedding_count: The number of low-confidence embeddings to store. + num_shards: The number of output shards. + """ + input_collection = ( + pipeline | 'ReadInputTFRecord' >> beam.io.tfrecordio.ReadFromTFRecord( + input_tfrecord, coder=beam.coders.BytesCoder()) + | 'AddKeys' >> beam.Map(add_keys)) + output_collection = input_collection | 'ExtractEmbedding' >> beam.ParDo( + GenerateEmbeddingDataFn(model_dir, top_k_embedding_count, + bottom_k_embedding_count)) + output_collection = output_collection | 'Reshuffle' >> beam.Reshuffle() + _ = output_collection | 'DropKeys' >> beam.Map( + drop_keys) | 'WritetoDisk' >> beam.io.tfrecordio.WriteToTFRecord( + output_tfrecord, + num_shards=num_shards, + coder=beam.coders.ProtoCoder(tf.train.Example)) + + +def parse_args(argv): + """Command-line argument parser. + + Args: + argv: command line arguments + Returns: + beam_args: Arguments for the beam pipeline. + pipeline_args: Arguments for the pipeline options, such as runner type. + """ + parser = argparse.ArgumentParser() + parser.add_argument( + '--embedding_input_tfrecord', + dest='embedding_input_tfrecord', + required=True, + help='TFRecord containing images in tf.Example format for object ' + 'detection.') + parser.add_argument( + '--embedding_output_tfrecord', + dest='embedding_output_tfrecord', + required=True, + help='TFRecord containing embeddings in tf.Example format.') + parser.add_argument( + '--embedding_model_dir', + dest='embedding_model_dir', + required=True, + help='Path to directory containing an object detection SavedModel with' + 'detection_box_classifier_features in the output.') + parser.add_argument( + '--top_k_embedding_count', + dest='top_k_embedding_count', + default=1, + help='The number of top k embeddings to add to the memory bank.') + parser.add_argument( + '--bottom_k_embedding_count', + dest='bottom_k_embedding_count', + default=0, + help='The number of bottom k embeddings to add to the memory bank.') + parser.add_argument( + '--num_shards', + dest='num_shards', + default=0, + help='Number of output shards.') + beam_args, pipeline_args = parser.parse_known_args(argv) + return beam_args, pipeline_args + + +def main(argv=None, save_main_session=True): + """Runs the Beam pipeline that performs inference. + + Args: + argv: Command line arguments. + save_main_session: Whether to save the main session. + """ + args, pipeline_args = parse_args(argv) + + pipeline_options = beam.options.pipeline_options.PipelineOptions( + pipeline_args) + pipeline_options.view_as( + beam.options.pipeline_options.SetupOptions).save_main_session = ( + save_main_session) + + dirname = os.path.dirname(args.embedding_output_tfrecord) + tf.io.gfile.makedirs(dirname) + + p = beam.Pipeline(options=pipeline_options) + + construct_pipeline( + p, + args.embedding_input_tfrecord, + args.embedding_output_tfrecord, + args.embedding_model_dir, + args.top_k_embedding_count, + args.bottom_k_embedding_count, + args.num_shards) + + p.run() + + +if __name__ == '__main__': + main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data_tf2_test.py b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data_tf2_test.py new file mode 100644 index 0000000..5566d6d --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/context_rcnn/generate_embedding_data_tf2_test.py @@ -0,0 +1,330 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for generate_embedding_data.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import contextlib +import os +import tempfile +import unittest +import numpy as np +import six +import tensorflow as tf +from object_detection import exporter_lib_v2 +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.protos import pipeline_pb2 +from object_detection.utils import tf_version + +if tf_version.is_tf2(): + from object_detection.dataset_tools.context_rcnn import generate_embedding_data # pylint:disable=g-import-not-at-top + +if six.PY2: + import mock # pylint: disable=g-import-not-at-top +else: + mock = unittest.mock + +try: + import apache_beam as beam # pylint:disable=g-import-not-at-top +except ModuleNotFoundError: + pass + + +class FakeModel(model.DetectionModel): + + def __init__(self, conv_weight_scalar=1.0): + super(FakeModel, self).__init__(num_classes=5) + self._conv = tf.keras.layers.Conv2D( + filters=1, kernel_size=1, strides=(1, 1), padding='valid', + kernel_initializer=tf.keras.initializers.Constant( + value=conv_weight_scalar)) + + def preprocess(self, inputs): + true_image_shapes = [] # Doesn't matter for the fake model. + return tf.identity(inputs), true_image_shapes + + def predict(self, preprocessed_inputs, true_image_shapes): + return {'image': self._conv(preprocessed_inputs)} + + def postprocess(self, prediction_dict, true_image_shapes): + with tf.control_dependencies(prediction_dict.values()): + num_features = 100 + feature_dims = 10 + classifier_feature = np.ones( + (2, feature_dims, feature_dims, num_features), + dtype=np.float32).tolist() + postprocessed_tensors = { + 'detection_boxes': tf.constant([[[0.0, 0.1, 0.5, 0.6], + [0.5, 0.5, 0.8, 0.8]]], tf.float32), + 'detection_scores': tf.constant([[0.95, 0.6]], tf.float32), + 'detection_multiclass_scores': tf.constant([[[0.1, 0.7, 0.2], + [0.3, 0.1, 0.6]]], + tf.float32), + 'detection_classes': tf.constant([[0, 1]], tf.float32), + 'num_detections': tf.constant([2], tf.float32), + 'detection_features': + tf.constant([classifier_feature], + tf.float32) + } + return postprocessed_tensors + + def restore_map(self, checkpoint_path, fine_tune_checkpoint_type): + pass + + def restore_from_objects(self, fine_tune_checkpoint_type): + pass + + def loss(self, prediction_dict, true_image_shapes): + pass + + def regularization_losses(self): + pass + + def updates(self): + pass + + +@contextlib.contextmanager +def InMemoryTFRecord(entries): + temp = tempfile.NamedTemporaryFile(delete=False) + filename = temp.name + try: + with tf.io.TFRecordWriter(filename) as writer: + for value in entries: + writer.write(value) + yield filename + finally: + os.unlink(temp.name) + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class GenerateEmbeddingData(tf.test.TestCase): + + def _save_checkpoint_from_mock_model(self, checkpoint_path): + """A function to save checkpoint from a fake Detection Model. + + Args: + checkpoint_path: Path to save checkpoint from Fake model. + """ + mock_model = FakeModel() + fake_image = tf.zeros(shape=[1, 10, 10, 3], dtype=tf.float32) + preprocessed_inputs, true_image_shapes = mock_model.preprocess(fake_image) + predictions = mock_model.predict(preprocessed_inputs, true_image_shapes) + mock_model.postprocess(predictions, true_image_shapes) + ckpt = tf.train.Checkpoint(model=mock_model) + exported_checkpoint_manager = tf.train.CheckpointManager( + ckpt, checkpoint_path, max_to_keep=1) + exported_checkpoint_manager.save(checkpoint_number=0) + + def _export_saved_model(self): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + output_directory = os.path.join(tmp_dir, 'output') + saved_model_path = os.path.join(output_directory, 'saved_model') + tf.io.gfile.makedirs(output_directory) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + exporter_lib_v2.INPUT_BUILDER_UTIL_MAP['model_build'] = mock_builder + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter_lib_v2.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory) + saved_model_path = os.path.join(output_directory, 'saved_model') + return saved_model_path + + def _create_tf_example(self): + encoded_image = tf.io.encode_jpeg( + tf.constant(np.ones((4, 4, 3)).astype(np.uint8))).numpy() + + def BytesFeature(value): + return tf.train.Feature(bytes_list=tf.train.BytesList(value=[value])) + + def Int64Feature(value): + return tf.train.Feature(int64_list=tf.train.Int64List(value=[value])) + + def FloatFeature(value): + return tf.train.Feature(float_list=tf.train.FloatList(value=[value])) + + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': BytesFeature(encoded_image), + 'image/source_id': BytesFeature(b'image_id'), + 'image/height': Int64Feature(400), + 'image/width': Int64Feature(600), + 'image/class/label': Int64Feature(5), + 'image/class/text': BytesFeature(b'hyena'), + 'image/object/bbox/xmin': FloatFeature(0.1), + 'image/object/bbox/xmax': FloatFeature(0.6), + 'image/object/bbox/ymin': FloatFeature(0.0), + 'image/object/bbox/ymax': FloatFeature(0.5), + 'image/object/class/score': FloatFeature(0.95), + 'image/object/class/label': Int64Feature(5), + 'image/object/class/text': BytesFeature(b'hyena'), + 'image/date_captured': BytesFeature(b'2019-10-20 12:12:12') + })) + + return example.SerializeToString() + + def assert_expected_example(self, example, topk=False, botk=False): + # Check embeddings + if topk or botk: + self.assertEqual(len( + example.features.feature['image/embedding'].float_list.value), + 218) + self.assertAllEqual( + example.features.feature['image/embedding_count'].int64_list.value, + [2]) + else: + self.assertEqual(len( + example.features.feature['image/embedding'].float_list.value), + 109) + self.assertAllEqual( + example.features.feature['image/embedding_count'].int64_list.value, + [1]) + + self.assertAllEqual( + example.features.feature['image/embedding_length'].int64_list.value, + [109]) + + # Check annotations + self.assertAllClose( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.0]) + self.assertAllClose( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.1]) + self.assertAllClose( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.5]) + self.assertAllClose( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.6]) + self.assertAllClose( + example.features.feature['image/object/class/score'] + .float_list.value, [0.95]) + self.assertAllClose( + example.features.feature['image/object/class/label'] + .int64_list.value, [5]) + self.assertAllEqual( + example.features.feature['image/object/class/text'] + .bytes_list.value, [b'hyena']) + self.assertAllClose( + example.features.feature['image/class/label'] + .int64_list.value, [5]) + self.assertAllEqual( + example.features.feature['image/class/text'] + .bytes_list.value, [b'hyena']) + + # Check other essential attributes. + self.assertAllEqual( + example.features.feature['image/height'].int64_list.value, [400]) + self.assertAllEqual( + example.features.feature['image/width'].int64_list.value, [600]) + self.assertAllEqual( + example.features.feature['image/source_id'].bytes_list.value, + [b'image_id']) + self.assertTrue( + example.features.feature['image/encoded'].bytes_list.value) + + def test_generate_embedding_data_fn(self): + saved_model_path = self._export_saved_model() + top_k_embedding_count = 1 + bottom_k_embedding_count = 0 + inference_fn = generate_embedding_data.GenerateEmbeddingDataFn( + saved_model_path, top_k_embedding_count, bottom_k_embedding_count) + inference_fn.setup() + generated_example = self._create_tf_example() + self.assertAllEqual(tf.train.Example.FromString( + generated_example).features.feature['image/object/class/label'] + .int64_list.value, [5]) + self.assertAllEqual(tf.train.Example.FromString( + generated_example).features.feature['image/object/class/text'] + .bytes_list.value, [b'hyena']) + output = inference_fn.process(('dummy_key', generated_example)) + output_example = output[0][1] + self.assert_expected_example(output_example) + + def test_generate_embedding_data_with_top_k_boxes(self): + saved_model_path = self._export_saved_model() + top_k_embedding_count = 2 + bottom_k_embedding_count = 0 + inference_fn = generate_embedding_data.GenerateEmbeddingDataFn( + saved_model_path, top_k_embedding_count, bottom_k_embedding_count) + inference_fn.setup() + generated_example = self._create_tf_example() + self.assertAllEqual( + tf.train.Example.FromString(generated_example).features + .feature['image/object/class/label'].int64_list.value, [5]) + self.assertAllEqual( + tf.train.Example.FromString(generated_example).features + .feature['image/object/class/text'].bytes_list.value, [b'hyena']) + output = inference_fn.process(('dummy_key', generated_example)) + output_example = output[0][1] + self.assert_expected_example(output_example, topk=True) + + def test_generate_embedding_data_with_bottom_k_boxes(self): + saved_model_path = self._export_saved_model() + top_k_embedding_count = 0 + bottom_k_embedding_count = 2 + inference_fn = generate_embedding_data.GenerateEmbeddingDataFn( + saved_model_path, top_k_embedding_count, bottom_k_embedding_count) + inference_fn.setup() + generated_example = self._create_tf_example() + self.assertAllEqual( + tf.train.Example.FromString(generated_example).features + .feature['image/object/class/label'].int64_list.value, [5]) + self.assertAllEqual( + tf.train.Example.FromString(generated_example).features + .feature['image/object/class/text'].bytes_list.value, [b'hyena']) + output = inference_fn.process(('dummy_key', generated_example)) + output_example = output[0][1] + self.assert_expected_example(output_example, botk=True) + + def test_beam_pipeline(self): + with InMemoryTFRecord([self._create_tf_example()]) as input_tfrecord: + temp_dir = tempfile.mkdtemp(dir=os.environ.get('TEST_TMPDIR')) + output_tfrecord = os.path.join(temp_dir, 'output_tfrecord') + saved_model_path = self._export_saved_model() + top_k_embedding_count = 1 + bottom_k_embedding_count = 0 + num_shards = 1 + pipeline_options = beam.options.pipeline_options.PipelineOptions( + runner='DirectRunner') + p = beam.Pipeline(options=pipeline_options) + generate_embedding_data.construct_pipeline( + p, input_tfrecord, output_tfrecord, saved_model_path, + top_k_embedding_count, bottom_k_embedding_count, num_shards) + p.run() + filenames = tf.io.gfile.glob( + output_tfrecord + '-?????-of-?????') + actual_output = [] + record_iterator = tf.data.TFRecordDataset( + tf.convert_to_tensor(filenames)).as_numpy_iterator() + for record in record_iterator: + actual_output.append(record) + self.assertEqual(len(actual_output), 1) + self.assert_expected_example(tf.train.Example.FromString( + actual_output[0])) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_ava_actions_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_ava_actions_tf_record.py new file mode 100644 index 0000000..a27001d --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_ava_actions_tf_record.py @@ -0,0 +1,540 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +r"""Code to download and parse the AVA Actions dataset for TensorFlow models. + +The [AVA Actions data set]( +https://research.google.com/ava/index.html) +is a dataset for human action recognition. + +This script downloads the annotations and prepares data from similar annotations +if local video files are available. The video files can be downloaded +from the following website: +https://github.com/cvdfoundation/ava-dataset + +Prior to running this script, please run download_and_preprocess_ava.sh to +download input videos. + +Running this code as a module generates the data set on disk. First, the +required files are downloaded (_download_data) which enables constructing the +label map. Then (in generate_examples), for each split in the data set, the +metadata and image frames are generated from the annotations for each sequence +example (_generate_examples). The data set is written to disk as a set of +numbered TFRecord files. + +Generating the data on disk can take considerable time and disk space. +(Image compression quality is the primary determiner of disk usage. + +If using the Tensorflow Object Detection API, set the input_type field +in the input_reader to TF_SEQUENCE_EXAMPLE. If using this script to generate +data for Context R-CNN scripts, the --examples_for_context flag should be +set to true, so that properly-formatted tf.example objects are written to disk. + +This data is structured for per-clip action classification where images is +the sequence of images and labels are a one-hot encoded value. See +as_dataset() for more details. + +Note that the number of videos changes in the data set over time, so it will +likely be necessary to change the expected number of examples. + +The argument video_path_format_string expects a value as such: + '/path/to/videos/{0}' + +""" +import collections +import contextlib +import csv +import glob +import hashlib +import os +import random +import sys +import zipfile + +from absl import app +from absl import flags +from absl import logging +import cv2 +from six.moves import range +from six.moves import urllib +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import seq_example_util +from object_detection.utils import dataset_util +from object_detection.utils import label_map_util + + +POSSIBLE_TIMESTAMPS = range(902, 1798) +ANNOTATION_URL = 'https://research.google.com/ava/download/ava_v2.2.zip' +SECONDS_TO_MILLI = 1000 +FILEPATTERN = 'ava_actions_%s_1fps_rgb' +SPLITS = { + 'train': { + 'shards': 1000, + 'examples': 862663, + 'csv': '', + 'excluded-csv': '' + }, + 'val': { + 'shards': 100, + 'examples': 243029, + 'csv': '', + 'excluded-csv': '' + }, + # Test doesn't have ground truth, so TF Records can't be created + 'test': { + 'shards': 100, + 'examples': 0, + 'csv': '', + 'excluded-csv': '' + } +} + +NUM_CLASSES = 80 + + +def feature_list_feature(value): + return tf.train.FeatureList(feature=value) + + +class Ava(object): + """Generates and loads the AVA Actions 2.2 data set.""" + + def __init__(self, path_to_output_dir, path_to_data_download): + if not path_to_output_dir: + raise ValueError('You must supply the path to the data directory.') + self.path_to_data_download = path_to_data_download + self.path_to_output_dir = path_to_output_dir + + def generate_and_write_records(self, + splits_to_process='train,val,test', + video_path_format_string=None, + seconds_per_sequence=10, + hop_between_sequences=10, + examples_for_context=False): + """Downloads data and generates sharded TFRecords. + + Downloads the data files, generates metadata, and processes the metadata + with MediaPipe to produce tf.SequenceExamples for training. The resulting + files can be read with as_dataset(). After running this function the + original data files can be deleted. + + Args: + splits_to_process: csv string of which splits to process. Allows + providing a custom CSV with the CSV flag. The original data is still + downloaded to generate the label_map. + video_path_format_string: The format string for the path to local files. + seconds_per_sequence: The length of each sequence, in seconds. + hop_between_sequences: The gap between the centers of + successive sequences. + examples_for_context: Whether to generate sequence examples with context + for context R-CNN. + """ + example_function = self._generate_sequence_examples + if examples_for_context: + example_function = self._generate_examples + + logging.info('Downloading data.') + download_output = self._download_data() + for key in splits_to_process.split(','): + logging.info('Generating examples for split: %s', key) + all_metadata = list(example_function( + download_output[0][key][0], download_output[0][key][1], + download_output[1], seconds_per_sequence, hop_between_sequences, + video_path_format_string)) + logging.info('An example of the metadata: ') + logging.info(all_metadata[0]) + random.seed(47) + random.shuffle(all_metadata) + shards = SPLITS[key]['shards'] + shard_names = [os.path.join( + self.path_to_output_dir, FILEPATTERN % key + '-%05d-of-%05d' % ( + i, shards)) for i in range(shards)] + writers = [tf.io.TFRecordWriter(shard) for shard in shard_names] + with _close_on_exit(writers) as writers: + for i, seq_ex in enumerate(all_metadata): + writers[i % len(writers)].write(seq_ex.SerializeToString()) + logging.info('Data extraction complete.') + + def _generate_sequence_examples(self, annotation_file, excluded_file, + label_map, seconds_per_sequence, + hop_between_sequences, + video_path_format_string): + """For each row in the annotation CSV, generates corresponding examples. + + When iterating through frames for a single sequence example, skips over + excluded frames. When moving to the next sequence example, also skips over + excluded frames as if they don't exist. Generates equal-length sequence + examples, each with length seconds_per_sequence (1 fps) and gaps of + hop_between_sequences frames (and seconds) between them, possible greater + due to excluded frames. + + Args: + annotation_file: path to the file of AVA CSV annotations. + excluded_file: path to a CSV file of excluded timestamps for each video. + label_map: an {int: string} label map. + seconds_per_sequence: The number of seconds per example in each example. + hop_between_sequences: The hop between sequences. If less than + seconds_per_sequence, will overlap. + video_path_format_string: File path format to glob video files. + + Yields: + Each prepared tf.SequenceExample of metadata also containing video frames + """ + fieldnames = ['id', 'timestamp_seconds', 'xmin', 'ymin', 'xmax', 'ymax', + 'action_label'] + frame_excluded = {} + # create a sparse, nested map of videos and frame indices. + with open(excluded_file, 'r') as excluded: + reader = csv.reader(excluded) + for row in reader: + frame_excluded[(row[0], int(float(row[1])))] = True + with open(annotation_file, 'r') as annotations: + reader = csv.DictReader(annotations, fieldnames) + frame_annotations = collections.defaultdict(list) + ids = set() + # aggreggate by video and timestamp: + for row in reader: + ids.add(row['id']) + key = (row['id'], int(float(row['timestamp_seconds']))) + frame_annotations[key].append(row) + # for each video, find aggregates near each sampled frame.: + logging.info('Generating metadata...') + media_num = 1 + for media_id in ids: + logging.info('%d/%d, ignore warnings.\n', media_num, len(ids)) + media_num += 1 + + filepath = glob.glob( + video_path_format_string.format(media_id) + '*')[0] + cur_vid = cv2.VideoCapture(filepath) + width = cur_vid.get(cv2.CAP_PROP_FRAME_WIDTH) + height = cur_vid.get(cv2.CAP_PROP_FRAME_HEIGHT) + middle_frame_time = POSSIBLE_TIMESTAMPS[0] + while middle_frame_time < POSSIBLE_TIMESTAMPS[-1]: + start_time = middle_frame_time - seconds_per_sequence // 2 - ( + 0 if seconds_per_sequence % 2 == 0 else 1) + end_time = middle_frame_time + (seconds_per_sequence // 2) + + total_boxes = [] + total_labels = [] + total_label_strings = [] + total_images = [] + total_source_ids = [] + total_confidences = [] + total_is_annotated = [] + windowed_timestamp = start_time + + while windowed_timestamp < end_time: + if (media_id, windowed_timestamp) in frame_excluded: + end_time += 1 + windowed_timestamp += 1 + logging.info('Ignoring and skipping excluded frame.') + continue + + cur_vid.set(cv2.CAP_PROP_POS_MSEC, + (windowed_timestamp) * SECONDS_TO_MILLI) + _, image = cur_vid.read() + _, buffer = cv2.imencode('.jpg', image) + + bufstring = buffer.tostring() + total_images.append(bufstring) + source_id = str(windowed_timestamp) + '_' + media_id + total_source_ids.append(source_id) + total_is_annotated.append(1) + + boxes = [] + labels = [] + label_strings = [] + confidences = [] + for row in frame_annotations[(media_id, windowed_timestamp)]: + if len(row) > 2 and int(row['action_label']) in label_map: + boxes.append([float(row['ymin']), float(row['xmin']), + float(row['ymax']), float(row['xmax'])]) + labels.append(int(row['action_label'])) + label_strings.append(label_map[int(row['action_label'])]) + confidences.append(1) + else: + logging.warning('Unknown label: %s', row['action_label']) + + total_boxes.append(boxes) + total_labels.append(labels) + total_label_strings.append(label_strings) + total_confidences.append(confidences) + windowed_timestamp += 1 + + if total_boxes: + yield seq_example_util.make_sequence_example( + 'AVA', media_id, total_images, int(height), int(width), 'jpeg', + total_source_ids, None, total_is_annotated, total_boxes, + total_label_strings, use_strs_for_source_id=True) + + # Move middle_time_frame, skipping excluded frames + frames_mv = 0 + frames_excluded_count = 0 + while (frames_mv < hop_between_sequences + frames_excluded_count + and middle_frame_time + frames_mv < POSSIBLE_TIMESTAMPS[-1]): + frames_mv += 1 + if (media_id, windowed_timestamp + frames_mv) in frame_excluded: + frames_excluded_count += 1 + middle_frame_time += frames_mv + + cur_vid.release() + + def _generate_examples(self, annotation_file, excluded_file, label_map, + seconds_per_sequence, hop_between_sequences, + video_path_format_string): + """For each row in the annotation CSV, generates examples. + + When iterating through frames for a single example, skips + over excluded frames. Generates equal-length sequence examples, each with + length seconds_per_sequence (1 fps) and gaps of hop_between_sequences + frames (and seconds) between them, possible greater due to excluded frames. + + Args: + annotation_file: path to the file of AVA CSV annotations. + excluded_file: path to a CSV file of excluded timestamps for each video. + label_map: an {int: string} label map. + seconds_per_sequence: The number of seconds per example in each example. + hop_between_sequences: The hop between sequences. If less than + seconds_per_sequence, will overlap. + video_path_format_string: File path format to glob video files. + + Yields: + Each prepared tf.Example of metadata also containing video frames + """ + del seconds_per_sequence + del hop_between_sequences + fieldnames = ['id', 'timestamp_seconds', 'xmin', 'ymin', 'xmax', 'ymax', + 'action_label'] + frame_excluded = {} + # create a sparse, nested map of videos and frame indices. + with open(excluded_file, 'r') as excluded: + reader = csv.reader(excluded) + for row in reader: + frame_excluded[(row[0], int(float(row[1])))] = True + with open(annotation_file, 'r') as annotations: + reader = csv.DictReader(annotations, fieldnames) + frame_annotations = collections.defaultdict(list) + ids = set() + # aggreggate by video and timestamp: + for row in reader: + ids.add(row['id']) + key = (row['id'], int(float(row['timestamp_seconds']))) + frame_annotations[key].append(row) + # for each video, find aggreggates near each sampled frame.: + logging.info('Generating metadata...') + media_num = 1 + for media_id in ids: + logging.info('%d/%d, ignore warnings.\n', media_num, len(ids)) + media_num += 1 + + filepath = glob.glob( + video_path_format_string.format(media_id) + '*')[0] + cur_vid = cv2.VideoCapture(filepath) + width = cur_vid.get(cv2.CAP_PROP_FRAME_WIDTH) + height = cur_vid.get(cv2.CAP_PROP_FRAME_HEIGHT) + middle_frame_time = POSSIBLE_TIMESTAMPS[0] + total_non_excluded = 0 + while middle_frame_time < POSSIBLE_TIMESTAMPS[-1]: + if (media_id, middle_frame_time) not in frame_excluded: + total_non_excluded += 1 + middle_frame_time += 1 + + middle_frame_time = POSSIBLE_TIMESTAMPS[0] + cur_frame_num = 0 + while middle_frame_time < POSSIBLE_TIMESTAMPS[-1]: + cur_vid.set(cv2.CAP_PROP_POS_MSEC, + middle_frame_time * SECONDS_TO_MILLI) + _, image = cur_vid.read() + _, buffer = cv2.imencode('.jpg', image) + + bufstring = buffer.tostring() + + if (media_id, middle_frame_time) in frame_excluded: + middle_frame_time += 1 + logging.info('Ignoring and skipping excluded frame.') + continue + + cur_frame_num += 1 + source_id = str(middle_frame_time) + '_' + media_id + + xmins = [] + xmaxs = [] + ymins = [] + ymaxs = [] + areas = [] + labels = [] + label_strings = [] + confidences = [] + for row in frame_annotations[(media_id, middle_frame_time)]: + if len(row) > 2 and int(row['action_label']) in label_map: + xmins.append(float(row['xmin'])) + xmaxs.append(float(row['xmax'])) + ymins.append(float(row['ymin'])) + ymaxs.append(float(row['ymax'])) + areas.append(float((xmaxs[-1] - xmins[-1]) * + (ymaxs[-1] - ymins[-1])) / 2) + labels.append(int(row['action_label'])) + label_strings.append(label_map[int(row['action_label'])]) + confidences.append(1) + else: + logging.warning('Unknown label: %s', row['action_label']) + + middle_frame_time += 1/3 + if abs(middle_frame_time - round(middle_frame_time) < 0.0001): + middle_frame_time = round(middle_frame_time) + + key = hashlib.sha256(bufstring).hexdigest() + date_captured_feature = ( + '2020-06-17 00:%02d:%02d' % ((middle_frame_time - 900)*3 // 60, + (middle_frame_time - 900)*3 % 60)) + context_feature_dict = { + 'image/height': + dataset_util.int64_feature(int(height)), + 'image/width': + dataset_util.int64_feature(int(width)), + 'image/format': + dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/source_id': + dataset_util.bytes_feature(source_id.encode('utf8')), + 'image/filename': + dataset_util.bytes_feature(source_id.encode('utf8')), + 'image/encoded': + dataset_util.bytes_feature(bufstring), + 'image/key/sha256': + dataset_util.bytes_feature(key.encode('utf8')), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(xmins), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(xmaxs), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(ymins), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(ymaxs), + 'image/object/area': + dataset_util.float_list_feature(areas), + 'image/object/class/label': + dataset_util.int64_list_feature(labels), + 'image/object/class/text': + dataset_util.bytes_list_feature(label_strings), + 'image/location': + dataset_util.bytes_feature(media_id.encode('utf8')), + 'image/date_captured': + dataset_util.bytes_feature( + date_captured_feature.encode('utf8')), + 'image/seq_num_frames': + dataset_util.int64_feature(total_non_excluded), + 'image/seq_frame_num': + dataset_util.int64_feature(cur_frame_num), + 'image/seq_id': + dataset_util.bytes_feature(media_id.encode('utf8')), + } + + yield tf.train.Example( + features=tf.train.Features(feature=context_feature_dict)) + + cur_vid.release() + + def _download_data(self): + """Downloads and extracts data if not already available.""" + if sys.version_info >= (3, 0): + urlretrieve = urllib.request.urlretrieve + else: + urlretrieve = urllib.request.urlretrieve + logging.info('Creating data directory.') + tf.io.gfile.makedirs(self.path_to_data_download) + logging.info('Downloading annotations.') + paths = {} + + zip_path = os.path.join(self.path_to_data_download, + ANNOTATION_URL.split('/')[-1]) + urlretrieve(ANNOTATION_URL, zip_path) + with zipfile.ZipFile(zip_path, 'r') as zip_ref: + zip_ref.extractall(self.path_to_data_download) + for split in ['train', 'test', 'val']: + csv_path = os.path.join(self.path_to_data_download, + 'ava_%s_v2.2.csv' % split) + excl_name = 'ava_%s_excluded_timestamps_v2.2.csv' % split + excluded_csv_path = os.path.join(self.path_to_data_download, excl_name) + SPLITS[split]['csv'] = csv_path + SPLITS[split]['excluded-csv'] = excluded_csv_path + paths[split] = (csv_path, excluded_csv_path) + + label_map = self.get_label_map(os.path.join( + self.path_to_data_download, + 'ava_action_list_v2.2_for_activitynet_2019.pbtxt')) + return paths, label_map + + def get_label_map(self, path): + """Parses a label map into {integer:string} format.""" + label_map_dict = label_map_util.get_label_map_dict(path) + label_map_dict = {v: bytes(k, 'utf8') for k, v in label_map_dict.items()} + logging.info(label_map_dict) + return label_map_dict + + +@contextlib.contextmanager +def _close_on_exit(writers): + """Call close on all writers on exit.""" + try: + yield writers + finally: + for writer in writers: + writer.close() + + +def main(argv): + if len(argv) > 1: + raise app.UsageError('Too many command-line arguments.') + Ava(flags.FLAGS.path_to_output_dir, + flags.FLAGS.path_to_download_data).generate_and_write_records( + flags.FLAGS.splits_to_process, + flags.FLAGS.video_path_format_string, + flags.FLAGS.seconds_per_sequence, + flags.FLAGS.hop_between_sequences, + flags.FLAGS.examples_for_context) + +if __name__ == '__main__': + flags.DEFINE_string('path_to_download_data', + '', + 'Path to directory to download data to.') + flags.DEFINE_string('path_to_output_dir', + '', + 'Path to directory to write data to.') + flags.DEFINE_string('splits_to_process', + 'train,val', + 'Process these splits. Useful for custom data splits.') + flags.DEFINE_string('video_path_format_string', + None, + 'The format string for the path to local video files. ' + 'Uses the Python string.format() syntax with possible ' + 'arguments of {video}, {start}, {end}, {label_name}, and ' + '{split}, corresponding to columns of the data csvs.') + flags.DEFINE_integer('seconds_per_sequence', + 10, + 'The number of seconds per example in each example.' + 'Always 1 when examples_for_context is True.') + flags.DEFINE_integer('hop_between_sequences', + 10, + 'The hop between sequences. If less than ' + 'seconds_per_sequence, will overlap. Always 1 when ' + 'examples_for_context is True.') + flags.DEFINE_boolean('examples_for_context', + False, + 'Whether to generate examples instead of sequence ' + 'examples. If true, will generate tf.Example objects ' + 'for use in Context R-CNN.') + app.run(main) diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record.py new file mode 100644 index 0000000..2703c42 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record.py @@ -0,0 +1,518 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Convert raw COCO dataset to TFRecord for object_detection. + +This tool supports data generation for object detection (boxes, masks), +keypoint detection, and DensePose. + +Please note that this tool creates sharded output files. + +Example usage: + python create_coco_tf_record.py --logtostderr \ + --train_image_dir="${TRAIN_IMAGE_DIR}" \ + --val_image_dir="${VAL_IMAGE_DIR}" \ + --test_image_dir="${TEST_IMAGE_DIR}" \ + --train_annotations_file="${TRAIN_ANNOTATIONS_FILE}" \ + --val_annotations_file="${VAL_ANNOTATIONS_FILE}" \ + --testdev_annotations_file="${TESTDEV_ANNOTATIONS_FILE}" \ + --output_dir="${OUTPUT_DIR}" +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import hashlib +import io +import json +import logging +import os +import contextlib2 +import numpy as np +import PIL.Image + +from pycocotools import mask +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import tf_record_creation_util +from object_detection.utils import dataset_util +from object_detection.utils import label_map_util + +flags = tf.app.flags +tf.flags.DEFINE_boolean( + 'include_masks', False, 'Whether to include instance segmentations masks ' + '(PNG encoded) in the result. default: False.') +tf.flags.DEFINE_string('train_image_dir', '', 'Training image directory.') +tf.flags.DEFINE_string('val_image_dir', '', 'Validation image directory.') +tf.flags.DEFINE_string('test_image_dir', '', 'Test image directory.') +tf.flags.DEFINE_string('train_annotations_file', '', + 'Training annotations JSON file.') +tf.flags.DEFINE_string('val_annotations_file', '', + 'Validation annotations JSON file.') +tf.flags.DEFINE_string('testdev_annotations_file', '', + 'Test-dev annotations JSON file.') +tf.flags.DEFINE_string('train_keypoint_annotations_file', '', + 'Training annotations JSON file.') +tf.flags.DEFINE_string('val_keypoint_annotations_file', '', + 'Validation annotations JSON file.') +# DensePose is only available for coco 2014. +tf.flags.DEFINE_string('train_densepose_annotations_file', '', + 'Training annotations JSON file for DensePose.') +tf.flags.DEFINE_string('val_densepose_annotations_file', '', + 'Validation annotations JSON file for DensePose.') +tf.flags.DEFINE_string('output_dir', '/tmp/', 'Output data directory.') +# Whether to only produce images/annotations on person class (for keypoint / +# densepose task). +tf.flags.DEFINE_boolean('remove_non_person_annotations', False, 'Whether to ' + 'remove all annotations for non-person objects.') +tf.flags.DEFINE_boolean('remove_non_person_images', False, 'Whether to ' + 'remove all examples that do not contain a person.') + +FLAGS = flags.FLAGS + +logger = tf.get_logger() +logger.setLevel(logging.INFO) + +_COCO_KEYPOINT_NAMES = [ + b'nose', b'left_eye', b'right_eye', b'left_ear', b'right_ear', + b'left_shoulder', b'right_shoulder', b'left_elbow', b'right_elbow', + b'left_wrist', b'right_wrist', b'left_hip', b'right_hip', + b'left_knee', b'right_knee', b'left_ankle', b'right_ankle' +] + +_COCO_PART_NAMES = [ + b'torso_back', b'torso_front', b'right_hand', b'left_hand', b'left_foot', + b'right_foot', b'right_upper_leg_back', b'left_upper_leg_back', + b'right_upper_leg_front', b'left_upper_leg_front', b'right_lower_leg_back', + b'left_lower_leg_back', b'right_lower_leg_front', b'left_lower_leg_front', + b'left_upper_arm_back', b'right_upper_arm_back', b'left_upper_arm_front', + b'right_upper_arm_front', b'left_lower_arm_back', b'right_lower_arm_back', + b'left_lower_arm_front', b'right_lower_arm_front', b'right_face', + b'left_face', +] + +_DP_PART_ID_OFFSET = 1 + + +def clip_to_unit(x): + return min(max(x, 0.0), 1.0) + + +def create_tf_example(image, + annotations_list, + image_dir, + category_index, + include_masks=False, + keypoint_annotations_dict=None, + densepose_annotations_dict=None, + remove_non_person_annotations=False, + remove_non_person_images=False): + """Converts image and annotations to a tf.Example proto. + + Args: + image: dict with keys: [u'license', u'file_name', u'coco_url', u'height', + u'width', u'date_captured', u'flickr_url', u'id'] + annotations_list: + list of dicts with keys: [u'segmentation', u'area', u'iscrowd', + u'image_id', u'bbox', u'category_id', u'id'] Notice that bounding box + coordinates in the official COCO dataset are given as [x, y, width, + height] tuples using absolute coordinates where x, y represent the + top-left (0-indexed) corner. This function converts to the format + expected by the Tensorflow Object Detection API (which is which is + [ymin, xmin, ymax, xmax] with coordinates normalized relative to image + size). + image_dir: directory containing the image files. + category_index: a dict containing COCO category information keyed by the + 'id' field of each category. See the label_map_util.create_category_index + function. + include_masks: Whether to include instance segmentations masks + (PNG encoded) in the result. default: False. + keypoint_annotations_dict: A dictionary that maps from annotation_id to a + dictionary with keys: [u'keypoints', u'num_keypoints'] represeting the + keypoint information for this person object annotation. If None, then + no keypoint annotations will be populated. + densepose_annotations_dict: A dictionary that maps from annotation_id to a + dictionary with keys: [u'dp_I', u'dp_x', u'dp_y', 'dp_U', 'dp_V'] + representing part surface coordinates. For more information see + http://densepose.org/. + remove_non_person_annotations: Whether to remove any annotations that are + not the "person" class. + remove_non_person_images: Whether to remove any images that do not contain + at least one "person" annotation. + + Returns: + key: SHA256 hash of the image. + example: The converted tf.Example + num_annotations_skipped: Number of (invalid) annotations that were ignored. + num_keypoint_annotation_skipped: Number of keypoint annotations that were + skipped. + num_densepose_annotation_skipped: Number of DensePose annotations that were + skipped. + + Raises: + ValueError: if the image pointed to by data['filename'] is not a valid JPEG + """ + image_height = image['height'] + image_width = image['width'] + filename = image['file_name'] + image_id = image['id'] + + full_path = os.path.join(image_dir, filename) + with tf.gfile.GFile(full_path, 'rb') as fid: + encoded_jpg = fid.read() + encoded_jpg_io = io.BytesIO(encoded_jpg) + image = PIL.Image.open(encoded_jpg_io) + key = hashlib.sha256(encoded_jpg).hexdigest() + + xmin = [] + xmax = [] + ymin = [] + ymax = [] + is_crowd = [] + category_names = [] + category_ids = [] + area = [] + encoded_mask_png = [] + keypoints_x = [] + keypoints_y = [] + keypoints_visibility = [] + keypoints_name = [] + num_keypoints = [] + include_keypoint = keypoint_annotations_dict is not None + num_annotations_skipped = 0 + num_keypoint_annotation_used = 0 + num_keypoint_annotation_skipped = 0 + dp_part_index = [] + dp_x = [] + dp_y = [] + dp_u = [] + dp_v = [] + dp_num_points = [] + densepose_keys = ['dp_I', 'dp_U', 'dp_V', 'dp_x', 'dp_y', 'bbox'] + include_densepose = densepose_annotations_dict is not None + num_densepose_annotation_used = 0 + num_densepose_annotation_skipped = 0 + for object_annotations in annotations_list: + (x, y, width, height) = tuple(object_annotations['bbox']) + if width <= 0 or height <= 0: + num_annotations_skipped += 1 + continue + if x + width > image_width or y + height > image_height: + num_annotations_skipped += 1 + continue + category_id = int(object_annotations['category_id']) + category_name = category_index[category_id]['name'].encode('utf8') + if remove_non_person_annotations and category_name != b'person': + num_annotations_skipped += 1 + continue + xmin.append(float(x) / image_width) + xmax.append(float(x + width) / image_width) + ymin.append(float(y) / image_height) + ymax.append(float(y + height) / image_height) + is_crowd.append(object_annotations['iscrowd']) + category_ids.append(category_id) + category_names.append(category_name) + area.append(object_annotations['area']) + + if include_masks: + run_len_encoding = mask.frPyObjects(object_annotations['segmentation'], + image_height, image_width) + binary_mask = mask.decode(run_len_encoding) + if not object_annotations['iscrowd']: + binary_mask = np.amax(binary_mask, axis=2) + pil_image = PIL.Image.fromarray(binary_mask) + output_io = io.BytesIO() + pil_image.save(output_io, format='PNG') + encoded_mask_png.append(output_io.getvalue()) + + if include_keypoint: + annotation_id = object_annotations['id'] + if annotation_id in keypoint_annotations_dict: + num_keypoint_annotation_used += 1 + keypoint_annotations = keypoint_annotations_dict[annotation_id] + keypoints = keypoint_annotations['keypoints'] + num_kpts = keypoint_annotations['num_keypoints'] + keypoints_x_abs = keypoints[::3] + keypoints_x.extend( + [float(x_abs) / image_width for x_abs in keypoints_x_abs]) + keypoints_y_abs = keypoints[1::3] + keypoints_y.extend( + [float(y_abs) / image_height for y_abs in keypoints_y_abs]) + keypoints_visibility.extend(keypoints[2::3]) + keypoints_name.extend(_COCO_KEYPOINT_NAMES) + num_keypoints.append(num_kpts) + else: + keypoints_x.extend([0.0] * len(_COCO_KEYPOINT_NAMES)) + keypoints_y.extend([0.0] * len(_COCO_KEYPOINT_NAMES)) + keypoints_visibility.extend([0] * len(_COCO_KEYPOINT_NAMES)) + keypoints_name.extend(_COCO_KEYPOINT_NAMES) + num_keypoints.append(0) + + if include_densepose: + annotation_id = object_annotations['id'] + if (annotation_id in densepose_annotations_dict and + all(key in densepose_annotations_dict[annotation_id] + for key in densepose_keys)): + dp_annotations = densepose_annotations_dict[annotation_id] + num_densepose_annotation_used += 1 + dp_num_points.append(len(dp_annotations['dp_I'])) + dp_part_index.extend([int(i - _DP_PART_ID_OFFSET) + for i in dp_annotations['dp_I']]) + # DensePose surface coordinates are defined on a [256, 256] grid + # relative to each instance box (i.e. absolute coordinates in range + # [0., 256.]). The following converts the coordinates + # so that they are expressed in normalized image coordinates. + dp_x_box_rel = [ + clip_to_unit(val / 256.) for val in dp_annotations['dp_x']] + dp_x_norm = [(float(x) + x_box_rel * width) / image_width + for x_box_rel in dp_x_box_rel] + dp_y_box_rel = [ + clip_to_unit(val / 256.) for val in dp_annotations['dp_y']] + dp_y_norm = [(float(y) + y_box_rel * height) / image_height + for y_box_rel in dp_y_box_rel] + dp_x.extend(dp_x_norm) + dp_y.extend(dp_y_norm) + dp_u.extend(dp_annotations['dp_U']) + dp_v.extend(dp_annotations['dp_V']) + else: + dp_num_points.append(0) + + if (remove_non_person_images and + not any(name == b'person' for name in category_names)): + return (key, None, num_annotations_skipped, + num_keypoint_annotation_skipped, num_densepose_annotation_skipped) + feature_dict = { + 'image/height': + dataset_util.int64_feature(image_height), + 'image/width': + dataset_util.int64_feature(image_width), + 'image/filename': + dataset_util.bytes_feature(filename.encode('utf8')), + 'image/source_id': + dataset_util.bytes_feature(str(image_id).encode('utf8')), + 'image/key/sha256': + dataset_util.bytes_feature(key.encode('utf8')), + 'image/encoded': + dataset_util.bytes_feature(encoded_jpg), + 'image/format': + dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/object/bbox/xmin': + dataset_util.float_list_feature(xmin), + 'image/object/bbox/xmax': + dataset_util.float_list_feature(xmax), + 'image/object/bbox/ymin': + dataset_util.float_list_feature(ymin), + 'image/object/bbox/ymax': + dataset_util.float_list_feature(ymax), + 'image/object/class/text': + dataset_util.bytes_list_feature(category_names), + 'image/object/is_crowd': + dataset_util.int64_list_feature(is_crowd), + 'image/object/area': + dataset_util.float_list_feature(area), + } + if include_masks: + feature_dict['image/object/mask'] = ( + dataset_util.bytes_list_feature(encoded_mask_png)) + if include_keypoint: + feature_dict['image/object/keypoint/x'] = ( + dataset_util.float_list_feature(keypoints_x)) + feature_dict['image/object/keypoint/y'] = ( + dataset_util.float_list_feature(keypoints_y)) + feature_dict['image/object/keypoint/num'] = ( + dataset_util.int64_list_feature(num_keypoints)) + feature_dict['image/object/keypoint/visibility'] = ( + dataset_util.int64_list_feature(keypoints_visibility)) + feature_dict['image/object/keypoint/text'] = ( + dataset_util.bytes_list_feature(keypoints_name)) + num_keypoint_annotation_skipped = ( + len(keypoint_annotations_dict) - num_keypoint_annotation_used) + if include_densepose: + feature_dict['image/object/densepose/num'] = ( + dataset_util.int64_list_feature(dp_num_points)) + feature_dict['image/object/densepose/part_index'] = ( + dataset_util.int64_list_feature(dp_part_index)) + feature_dict['image/object/densepose/x'] = ( + dataset_util.float_list_feature(dp_x)) + feature_dict['image/object/densepose/y'] = ( + dataset_util.float_list_feature(dp_y)) + feature_dict['image/object/densepose/u'] = ( + dataset_util.float_list_feature(dp_u)) + feature_dict['image/object/densepose/v'] = ( + dataset_util.float_list_feature(dp_v)) + num_densepose_annotation_skipped = ( + len(densepose_annotations_dict) - num_densepose_annotation_used) + + example = tf.train.Example(features=tf.train.Features(feature=feature_dict)) + return (key, example, num_annotations_skipped, + num_keypoint_annotation_skipped, num_densepose_annotation_skipped) + + +def _create_tf_record_from_coco_annotations(annotations_file, image_dir, + output_path, include_masks, + num_shards, + keypoint_annotations_file='', + densepose_annotations_file='', + remove_non_person_annotations=False, + remove_non_person_images=False): + """Loads COCO annotation json files and converts to tf.Record format. + + Args: + annotations_file: JSON file containing bounding box annotations. + image_dir: Directory containing the image files. + output_path: Path to output tf.Record file. + include_masks: Whether to include instance segmentations masks + (PNG encoded) in the result. default: False. + num_shards: number of output file shards. + keypoint_annotations_file: JSON file containing the person keypoint + annotations. If empty, then no person keypoint annotations will be + generated. + densepose_annotations_file: JSON file containing the DensePose annotations. + If empty, then no DensePose annotations will be generated. + remove_non_person_annotations: Whether to remove any annotations that are + not the "person" class. + remove_non_person_images: Whether to remove any images that do not contain + at least one "person" annotation. + """ + with contextlib2.ExitStack() as tf_record_close_stack, \ + tf.gfile.GFile(annotations_file, 'r') as fid: + output_tfrecords = tf_record_creation_util.open_sharded_output_tfrecords( + tf_record_close_stack, output_path, num_shards) + groundtruth_data = json.load(fid) + images = groundtruth_data['images'] + category_index = label_map_util.create_category_index( + groundtruth_data['categories']) + + annotations_index = {} + if 'annotations' in groundtruth_data: + logging.info('Found groundtruth annotations. Building annotations index.') + for annotation in groundtruth_data['annotations']: + image_id = annotation['image_id'] + if image_id not in annotations_index: + annotations_index[image_id] = [] + annotations_index[image_id].append(annotation) + missing_annotation_count = 0 + for image in images: + image_id = image['id'] + if image_id not in annotations_index: + missing_annotation_count += 1 + annotations_index[image_id] = [] + logging.info('%d images are missing annotations.', + missing_annotation_count) + + keypoint_annotations_index = {} + if keypoint_annotations_file: + with tf.gfile.GFile(keypoint_annotations_file, 'r') as kid: + keypoint_groundtruth_data = json.load(kid) + if 'annotations' in keypoint_groundtruth_data: + for annotation in keypoint_groundtruth_data['annotations']: + image_id = annotation['image_id'] + if image_id not in keypoint_annotations_index: + keypoint_annotations_index[image_id] = {} + keypoint_annotations_index[image_id][annotation['id']] = annotation + + densepose_annotations_index = {} + if densepose_annotations_file: + with tf.gfile.GFile(densepose_annotations_file, 'r') as fid: + densepose_groundtruth_data = json.load(fid) + if 'annotations' in densepose_groundtruth_data: + for annotation in densepose_groundtruth_data['annotations']: + image_id = annotation['image_id'] + if image_id not in densepose_annotations_index: + densepose_annotations_index[image_id] = {} + densepose_annotations_index[image_id][annotation['id']] = annotation + + total_num_annotations_skipped = 0 + total_num_keypoint_annotations_skipped = 0 + total_num_densepose_annotations_skipped = 0 + for idx, image in enumerate(images): + if idx % 100 == 0: + logging.info('On image %d of %d', idx, len(images)) + annotations_list = annotations_index[image['id']] + keypoint_annotations_dict = None + if keypoint_annotations_file: + keypoint_annotations_dict = {} + if image['id'] in keypoint_annotations_index: + keypoint_annotations_dict = keypoint_annotations_index[image['id']] + densepose_annotations_dict = None + if densepose_annotations_file: + densepose_annotations_dict = {} + if image['id'] in densepose_annotations_index: + densepose_annotations_dict = densepose_annotations_index[image['id']] + (_, tf_example, num_annotations_skipped, num_keypoint_annotations_skipped, + num_densepose_annotations_skipped) = create_tf_example( + image, annotations_list, image_dir, category_index, include_masks, + keypoint_annotations_dict, densepose_annotations_dict, + remove_non_person_annotations, remove_non_person_images) + total_num_annotations_skipped += num_annotations_skipped + total_num_keypoint_annotations_skipped += num_keypoint_annotations_skipped + total_num_densepose_annotations_skipped += ( + num_densepose_annotations_skipped) + shard_idx = idx % num_shards + if tf_example: + output_tfrecords[shard_idx].write(tf_example.SerializeToString()) + logging.info('Finished writing, skipped %d annotations.', + total_num_annotations_skipped) + if keypoint_annotations_file: + logging.info('Finished writing, skipped %d keypoint annotations.', + total_num_keypoint_annotations_skipped) + if densepose_annotations_file: + logging.info('Finished writing, skipped %d DensePose annotations.', + total_num_densepose_annotations_skipped) + + +def main(_): + assert FLAGS.train_image_dir, '`train_image_dir` missing.' + assert FLAGS.val_image_dir, '`val_image_dir` missing.' + assert FLAGS.test_image_dir, '`test_image_dir` missing.' + assert FLAGS.train_annotations_file, '`train_annotations_file` missing.' + assert FLAGS.val_annotations_file, '`val_annotations_file` missing.' + assert FLAGS.testdev_annotations_file, '`testdev_annotations_file` missing.' + + if not tf.gfile.IsDirectory(FLAGS.output_dir): + tf.gfile.MakeDirs(FLAGS.output_dir) + train_output_path = os.path.join(FLAGS.output_dir, 'coco_train.record') + val_output_path = os.path.join(FLAGS.output_dir, 'coco_val.record') + testdev_output_path = os.path.join(FLAGS.output_dir, 'coco_testdev.record') + + _create_tf_record_from_coco_annotations( + FLAGS.train_annotations_file, + FLAGS.train_image_dir, + train_output_path, + FLAGS.include_masks, + num_shards=100, + keypoint_annotations_file=FLAGS.train_keypoint_annotations_file, + densepose_annotations_file=FLAGS.train_densepose_annotations_file, + remove_non_person_annotations=FLAGS.remove_non_person_annotations, + remove_non_person_images=FLAGS.remove_non_person_images) + _create_tf_record_from_coco_annotations( + FLAGS.val_annotations_file, + FLAGS.val_image_dir, + val_output_path, + FLAGS.include_masks, + num_shards=50, + keypoint_annotations_file=FLAGS.val_keypoint_annotations_file, + densepose_annotations_file=FLAGS.val_densepose_annotations_file, + remove_non_person_annotations=FLAGS.remove_non_person_annotations, + remove_non_person_images=FLAGS.remove_non_person_images) + _create_tf_record_from_coco_annotations( + FLAGS.testdev_annotations_file, + FLAGS.test_image_dir, + testdev_output_path, + FLAGS.include_masks, + num_shards=50) + + +if __name__ == '__main__': + tf.app.run() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record_test.py b/workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record_test.py new file mode 100644 index 0000000..659142b --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_coco_tf_record_test.py @@ -0,0 +1,497 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Test for create_coco_tf_record.py.""" + +import io +import json +import os + +import numpy as np +import PIL.Image +import six +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import create_coco_tf_record + + +class CreateCocoTFRecordTest(tf.test.TestCase): + + def _assertProtoEqual(self, proto_field, expectation): + """Helper function to assert if a proto field equals some value. + + Args: + proto_field: The protobuf field to compare. + expectation: The expected value of the protobuf field. + """ + proto_list = [p for p in proto_field] + self.assertListEqual(proto_list, expectation) + + def _assertProtoClose(self, proto_field, expectation): + """Helper function to assert if a proto field nearly equals some value. + + Args: + proto_field: The protobuf field to compare. + expectation: The expected value of the protobuf field. + """ + proto_list = [p for p in proto_field] + self.assertAllClose(proto_list, expectation) + + def test_create_tf_example(self): + image_file_name = 'tmp_image.jpg' + image_data = np.random.rand(256, 256, 3) + tmp_dir = self.get_temp_dir() + save_path = os.path.join(tmp_dir, image_file_name) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + image = { + 'file_name': image_file_name, + 'height': 256, + 'width': 256, + 'id': 11, + } + + annotations_list = [{ + 'area': .5, + 'iscrowd': False, + 'image_id': 11, + 'bbox': [64, 64, 128, 128], + 'category_id': 2, + 'id': 1000, + }] + + image_dir = tmp_dir + category_index = { + 1: { + 'name': 'dog', + 'id': 1 + }, + 2: { + 'name': 'cat', + 'id': 2 + }, + 3: { + 'name': 'human', + 'id': 3 + } + } + + (_, example, + num_annotations_skipped, _, _) = create_coco_tf_record.create_tf_example( + image, annotations_list, image_dir, category_index) + + self.assertEqual(num_annotations_skipped, 0) + self._assertProtoEqual( + example.features.feature['image/height'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/width'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/filename'].bytes_list.value, + [six.b(image_file_name)]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [six.b(str(image['id']))]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, + [six.b('jpeg')]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/class/text'].bytes_list.value, + [six.b('cat')]) + + def test_create_tf_example_with_instance_masks(self): + image_file_name = 'tmp_image.jpg' + image_data = np.random.rand(8, 8, 3) + tmp_dir = self.get_temp_dir() + save_path = os.path.join(tmp_dir, image_file_name) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + image = { + 'file_name': image_file_name, + 'height': 8, + 'width': 8, + 'id': 11, + } + + annotations_list = [{ + 'area': .5, + 'iscrowd': False, + 'image_id': 11, + 'bbox': [0, 0, 8, 8], + 'segmentation': [[4, 0, 0, 0, 0, 4], [8, 4, 4, 8, 8, 8]], + 'category_id': 1, + 'id': 1000, + }] + + image_dir = tmp_dir + category_index = { + 1: { + 'name': 'dog', + 'id': 1 + }, + } + + (_, example, + num_annotations_skipped, _, _) = create_coco_tf_record.create_tf_example( + image, annotations_list, image_dir, category_index, include_masks=True) + + self.assertEqual(num_annotations_skipped, 0) + self._assertProtoEqual( + example.features.feature['image/height'].int64_list.value, [8]) + self._assertProtoEqual( + example.features.feature['image/width'].int64_list.value, [8]) + self._assertProtoEqual( + example.features.feature['image/filename'].bytes_list.value, + [six.b(image_file_name)]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [six.b(str(image['id']))]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, + [six.b('jpeg')]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [1]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [1]) + self._assertProtoEqual( + example.features.feature['image/object/class/text'].bytes_list.value, + [six.b('dog')]) + encoded_mask_pngs = [ + io.BytesIO(encoded_masks) for encoded_masks in example.features.feature[ + 'image/object/mask'].bytes_list.value + ] + pil_masks = [ + np.array(PIL.Image.open(encoded_mask_png)) + for encoded_mask_png in encoded_mask_pngs + ] + self.assertEqual(len(pil_masks), 1) + self.assertAllEqual(pil_masks[0], + [[1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 1, 1], + [0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1]]) + + def test_create_tf_example_with_keypoints(self): + image_dir = self.get_temp_dir() + image_file_name = 'tmp_image.jpg' + image_data = np.random.randint(low=0, high=256, size=(256, 256, 3)).astype( + np.uint8) + save_path = os.path.join(image_dir, image_file_name) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + image = { + 'file_name': image_file_name, + 'height': 256, + 'width': 256, + 'id': 11, + } + + min_x, min_y = 64, 64 + max_x, max_y = 128, 128 + keypoints = [] + num_visible_keypoints = 0 + xv = [] + yv = [] + vv = [] + for _ in range(17): + xc = min_x + int(np.random.rand()*(max_x - min_x)) + yc = min_y + int(np.random.rand()*(max_y - min_y)) + vis = np.random.randint(0, 3) + xv.append(xc) + yv.append(yc) + vv.append(vis) + keypoints.extend([xc, yc, vis]) + num_visible_keypoints += (vis > 0) + + annotations_list = [{ + 'area': 0.5, + 'iscrowd': False, + 'image_id': 11, + 'bbox': [64, 64, 128, 128], + 'category_id': 1, + 'id': 1000 + }] + + keypoint_annotations_dict = { + 1000: { + 'keypoints': keypoints, + 'num_keypoints': num_visible_keypoints + } + } + + category_index = { + 1: { + 'name': 'person', + 'id': 1 + } + } + + _, example, _, num_keypoint_annotation_skipped, _ = ( + create_coco_tf_record.create_tf_example( + image, + annotations_list, + image_dir, + category_index, + include_masks=False, + keypoint_annotations_dict=keypoint_annotations_dict)) + + self.assertEqual(num_keypoint_annotation_skipped, 0) + self._assertProtoEqual( + example.features.feature['image/height'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/width'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/filename'].bytes_list.value, + [six.b(image_file_name)]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [six.b(str(image['id']))]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, + [six.b('jpeg')]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/class/text'].bytes_list.value, + [six.b('person')]) + self._assertProtoClose( + example.features.feature['image/object/keypoint/x'].float_list.value, + np.array(xv, dtype=np.float32) / 256) + self._assertProtoClose( + example.features.feature['image/object/keypoint/y'].float_list.value, + np.array(yv, dtype=np.float32) / 256) + self._assertProtoEqual( + example.features.feature['image/object/keypoint/text'].bytes_list.value, + create_coco_tf_record._COCO_KEYPOINT_NAMES) + self._assertProtoEqual( + example.features.feature[ + 'image/object/keypoint/visibility'].int64_list.value, vv) + + def test_create_tf_example_with_dense_pose(self): + image_dir = self.get_temp_dir() + image_file_name = 'tmp_image.jpg' + image_data = np.random.randint(low=0, high=256, size=(256, 256, 3)).astype( + np.uint8) + save_path = os.path.join(image_dir, image_file_name) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + image = { + 'file_name': image_file_name, + 'height': 256, + 'width': 256, + 'id': 11, + } + + min_x, min_y = 64, 64 + max_x, max_y = 128, 128 + keypoints = [] + num_visible_keypoints = 0 + xv = [] + yv = [] + vv = [] + for _ in range(17): + xc = min_x + int(np.random.rand()*(max_x - min_x)) + yc = min_y + int(np.random.rand()*(max_y - min_y)) + vis = np.random.randint(0, 3) + xv.append(xc) + yv.append(yc) + vv.append(vis) + keypoints.extend([xc, yc, vis]) + num_visible_keypoints += (vis > 0) + + annotations_list = [{ + 'area': 0.5, + 'iscrowd': False, + 'image_id': 11, + 'bbox': [64, 64, 128, 128], + 'category_id': 1, + 'id': 1000 + }] + + num_points = 45 + dp_i = np.random.randint(1, 25, (num_points,)).astype(np.float32) + dp_u = np.random.randn(num_points) + dp_v = np.random.randn(num_points) + dp_x = np.random.rand(num_points)*256. + dp_y = np.random.rand(num_points)*256. + densepose_annotations_dict = { + 1000: { + 'dp_I': dp_i, + 'dp_U': dp_u, + 'dp_V': dp_v, + 'dp_x': dp_x, + 'dp_y': dp_y, + 'bbox': [64, 64, 128, 128], + } + } + + category_index = { + 1: { + 'name': 'person', + 'id': 1 + } + } + + _, example, _, _, num_densepose_annotation_skipped = ( + create_coco_tf_record.create_tf_example( + image, + annotations_list, + image_dir, + category_index, + include_masks=False, + densepose_annotations_dict=densepose_annotations_dict)) + + self.assertEqual(num_densepose_annotation_skipped, 0) + self._assertProtoEqual( + example.features.feature['image/height'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/width'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/filename'].bytes_list.value, + [six.b(image_file_name)]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [six.b(str(image['id']))]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, + [six.b('jpeg')]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/class/text'].bytes_list.value, + [six.b('person')]) + self._assertProtoEqual( + example.features.feature['image/object/densepose/num'].int64_list.value, + [num_points]) + self.assertAllEqual( + example.features.feature[ + 'image/object/densepose/part_index'].int64_list.value, + dp_i.astype(np.int64) - create_coco_tf_record._DP_PART_ID_OFFSET) + self.assertAllClose( + example.features.feature['image/object/densepose/u'].float_list.value, + dp_u) + self.assertAllClose( + example.features.feature['image/object/densepose/v'].float_list.value, + dp_v) + expected_dp_x = (64 + dp_x * 128. / 256.) / 256. + expected_dp_y = (64 + dp_y * 128. / 256.) / 256. + self.assertAllClose( + example.features.feature['image/object/densepose/x'].float_list.value, + expected_dp_x) + self.assertAllClose( + example.features.feature['image/object/densepose/y'].float_list.value, + expected_dp_y) + + def test_create_sharded_tf_record(self): + tmp_dir = self.get_temp_dir() + image_paths = ['tmp1_image.jpg', 'tmp2_image.jpg'] + for image_path in image_paths: + image_data = np.random.rand(256, 256, 3) + save_path = os.path.join(tmp_dir, image_path) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + images = [{ + 'file_name': image_paths[0], + 'height': 256, + 'width': 256, + 'id': 11, + }, { + 'file_name': image_paths[1], + 'height': 256, + 'width': 256, + 'id': 12, + }] + + annotations = [{ + 'area': .5, + 'iscrowd': False, + 'image_id': 11, + 'bbox': [64, 64, 128, 128], + 'category_id': 2, + 'id': 1000, + }] + + category_index = [{ + 'name': 'dog', + 'id': 1 + }, { + 'name': 'cat', + 'id': 2 + }, { + 'name': 'human', + 'id': 3 + }] + groundtruth_data = {'images': images, 'annotations': annotations, + 'categories': category_index} + annotation_file = os.path.join(tmp_dir, 'annotation.json') + with open(annotation_file, 'w') as annotation_fid: + json.dump(groundtruth_data, annotation_fid) + + output_path = os.path.join(tmp_dir, 'out.record') + create_coco_tf_record._create_tf_record_from_coco_annotations( + annotation_file, + tmp_dir, + output_path, + False, + 2) + self.assertTrue(os.path.exists(output_path + '-00000-of-00002')) + self.assertTrue(os.path.exists(output_path + '-00001-of-00002')) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record.py new file mode 100644 index 0000000..fe4f13e --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record.py @@ -0,0 +1,310 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +r"""Convert raw KITTI detection dataset to TFRecord for object_detection. + +Converts KITTI detection dataset to TFRecords with a standard format allowing + to use this dataset to train object detectors. The raw dataset can be + downloaded from: + http://kitti.is.tue.mpg.de/kitti/data_object_image_2.zip. + http://kitti.is.tue.mpg.de/kitti/data_object_label_2.zip + Permission can be requested at the main website. + + KITTI detection dataset contains 7481 training images. Using this code with + the default settings will set aside the first 500 images as a validation set. + This can be altered using the flags, see details below. + +Example usage: + python object_detection/dataset_tools/create_kitti_tf_record.py \ + --data_dir=/home/user/kitti \ + --output_path=/home/user/kitti.record +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +import hashlib +import io +import os + +import numpy as np +import PIL.Image as pil +import tensorflow.compat.v1 as tf + +from object_detection.utils import dataset_util +from object_detection.utils import label_map_util +from object_detection.utils.np_box_ops import iou + +tf.app.flags.DEFINE_string('data_dir', '', 'Location of root directory for the ' + 'data. Folder structure is assumed to be:' + '/training/label_2 (annotations) and' + '/data_object_image_2/training/image_2' + '(images).') +tf.app.flags.DEFINE_string('output_path', '', 'Path to which TFRecord files' + 'will be written. The TFRecord with the training set' + 'will be located at: _train.tfrecord.' + 'And the TFRecord with the validation set will be' + 'located at: _val.tfrecord') +tf.app.flags.DEFINE_string('classes_to_use', 'car,pedestrian,dontcare', + 'Comma separated list of class names that will be' + 'used. Adding the dontcare class will remove all' + 'bboxs in the dontcare regions.') +tf.app.flags.DEFINE_string('label_map_path', 'data/kitti_label_map.pbtxt', + 'Path to label map proto.') +tf.app.flags.DEFINE_integer('validation_set_size', '500', 'Number of images to' + 'be used as a validation set.') +FLAGS = tf.app.flags.FLAGS + + +def convert_kitti_to_tfrecords(data_dir, output_path, classes_to_use, + label_map_path, validation_set_size): + """Convert the KITTI detection dataset to TFRecords. + + Args: + data_dir: The full path to the unzipped folder containing the unzipped data + from data_object_image_2 and data_object_label_2.zip. + Folder structure is assumed to be: data_dir/training/label_2 (annotations) + and data_dir/data_object_image_2/training/image_2 (images). + output_path: The path to which TFRecord files will be written. The TFRecord + with the training set will be located at: _train.tfrecord + And the TFRecord with the validation set will be located at: + _val.tfrecord + classes_to_use: List of strings naming the classes for which data should be + converted. Use the same names as presented in the KIITI README file. + Adding dontcare class will remove all other bounding boxes that overlap + with areas marked as dontcare regions. + label_map_path: Path to label map proto + validation_set_size: How many images should be left as the validation set. + (Ffirst `validation_set_size` examples are selected to be in the + validation set). + """ + label_map_dict = label_map_util.get_label_map_dict(label_map_path) + train_count = 0 + val_count = 0 + + annotation_dir = os.path.join(data_dir, + 'training', + 'label_2') + + image_dir = os.path.join(data_dir, + 'data_object_image_2', + 'training', + 'image_2') + + train_writer = tf.python_io.TFRecordWriter('%s_train.tfrecord'% + output_path) + val_writer = tf.python_io.TFRecordWriter('%s_val.tfrecord'% + output_path) + + images = sorted(tf.gfile.ListDirectory(image_dir)) + for img_name in images: + img_num = int(img_name.split('.')[0]) + is_validation_img = img_num < validation_set_size + img_anno = read_annotation_file(os.path.join(annotation_dir, + str(img_num).zfill(6)+'.txt')) + + image_path = os.path.join(image_dir, img_name) + + # Filter all bounding boxes of this frame that are of a legal class, and + # don't overlap with a dontcare region. + # TODO(talremez) filter out targets that are truncated or heavily occluded. + annotation_for_image = filter_annotations(img_anno, classes_to_use) + + example = prepare_example(image_path, annotation_for_image, label_map_dict) + if is_validation_img: + val_writer.write(example.SerializeToString()) + val_count += 1 + else: + train_writer.write(example.SerializeToString()) + train_count += 1 + + train_writer.close() + val_writer.close() + + +def prepare_example(image_path, annotations, label_map_dict): + """Converts a dictionary with annotations for an image to tf.Example proto. + + Args: + image_path: The complete path to image. + annotations: A dictionary representing the annotation of a single object + that appears in the image. + label_map_dict: A map from string label names to integer ids. + + Returns: + example: The converted tf.Example. + """ + with tf.gfile.GFile(image_path, 'rb') as fid: + encoded_png = fid.read() + encoded_png_io = io.BytesIO(encoded_png) + image = pil.open(encoded_png_io) + image = np.asarray(image) + + key = hashlib.sha256(encoded_png).hexdigest() + + width = int(image.shape[1]) + height = int(image.shape[0]) + + xmin_norm = annotations['2d_bbox_left'] / float(width) + ymin_norm = annotations['2d_bbox_top'] / float(height) + xmax_norm = annotations['2d_bbox_right'] / float(width) + ymax_norm = annotations['2d_bbox_bottom'] / float(height) + + difficult_obj = [0]*len(xmin_norm) + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/height': dataset_util.int64_feature(height), + 'image/width': dataset_util.int64_feature(width), + 'image/filename': dataset_util.bytes_feature(image_path.encode('utf8')), + 'image/source_id': dataset_util.bytes_feature(image_path.encode('utf8')), + 'image/key/sha256': dataset_util.bytes_feature(key.encode('utf8')), + 'image/encoded': dataset_util.bytes_feature(encoded_png), + 'image/format': dataset_util.bytes_feature('png'.encode('utf8')), + 'image/object/bbox/xmin': dataset_util.float_list_feature(xmin_norm), + 'image/object/bbox/xmax': dataset_util.float_list_feature(xmax_norm), + 'image/object/bbox/ymin': dataset_util.float_list_feature(ymin_norm), + 'image/object/bbox/ymax': dataset_util.float_list_feature(ymax_norm), + 'image/object/class/text': dataset_util.bytes_list_feature( + [x.encode('utf8') for x in annotations['type']]), + 'image/object/class/label': dataset_util.int64_list_feature( + [label_map_dict[x] for x in annotations['type']]), + 'image/object/difficult': dataset_util.int64_list_feature(difficult_obj), + 'image/object/truncated': dataset_util.float_list_feature( + annotations['truncated']), + 'image/object/alpha': dataset_util.float_list_feature( + annotations['alpha']), + 'image/object/3d_bbox/height': dataset_util.float_list_feature( + annotations['3d_bbox_height']), + 'image/object/3d_bbox/width': dataset_util.float_list_feature( + annotations['3d_bbox_width']), + 'image/object/3d_bbox/length': dataset_util.float_list_feature( + annotations['3d_bbox_length']), + 'image/object/3d_bbox/x': dataset_util.float_list_feature( + annotations['3d_bbox_x']), + 'image/object/3d_bbox/y': dataset_util.float_list_feature( + annotations['3d_bbox_y']), + 'image/object/3d_bbox/z': dataset_util.float_list_feature( + annotations['3d_bbox_z']), + 'image/object/3d_bbox/rot_y': dataset_util.float_list_feature( + annotations['3d_bbox_rot_y']), + })) + + return example + + +def filter_annotations(img_all_annotations, used_classes): + """Filters out annotations from the unused classes and dontcare regions. + + Filters out the annotations that belong to classes we do now wish to use and + (optionally) also removes all boxes that overlap with dontcare regions. + + Args: + img_all_annotations: A list of annotation dictionaries. See documentation of + read_annotation_file for more details about the format of the annotations. + used_classes: A list of strings listing the classes we want to keep, if the + list contains "dontcare", all bounding boxes with overlapping with dont + care regions will also be filtered out. + + Returns: + img_filtered_annotations: A list of annotation dictionaries that have passed + the filtering. + """ + + img_filtered_annotations = {} + + # Filter the type of the objects. + relevant_annotation_indices = [ + i for i, x in enumerate(img_all_annotations['type']) if x in used_classes + ] + + for key in img_all_annotations.keys(): + img_filtered_annotations[key] = ( + img_all_annotations[key][relevant_annotation_indices]) + + if 'dontcare' in used_classes: + dont_care_indices = [i for i, + x in enumerate(img_filtered_annotations['type']) + if x == 'dontcare'] + + # bounding box format [y_min, x_min, y_max, x_max] + all_boxes = np.stack([img_filtered_annotations['2d_bbox_top'], + img_filtered_annotations['2d_bbox_left'], + img_filtered_annotations['2d_bbox_bottom'], + img_filtered_annotations['2d_bbox_right']], + axis=1) + + ious = iou(boxes1=all_boxes, + boxes2=all_boxes[dont_care_indices]) + + # Remove all bounding boxes that overlap with a dontcare region. + if ious.size > 0: + boxes_to_remove = np.amax(ious, axis=1) > 0.0 + for key in img_all_annotations.keys(): + img_filtered_annotations[key] = ( + img_filtered_annotations[key][np.logical_not(boxes_to_remove)]) + + return img_filtered_annotations + + +def read_annotation_file(filename): + """Reads a KITTI annotation file. + + Converts a KITTI annotation file into a dictionary containing all the + relevant information. + + Args: + filename: the path to the annotataion text file. + + Returns: + anno: A dictionary with the converted annotation information. See annotation + README file for details on the different fields. + """ + with open(filename) as f: + content = f.readlines() + content = [x.strip().split(' ') for x in content] + + anno = {} + anno['type'] = np.array([x[0].lower() for x in content]) + anno['truncated'] = np.array([float(x[1]) for x in content]) + anno['occluded'] = np.array([int(x[2]) for x in content]) + anno['alpha'] = np.array([float(x[3]) for x in content]) + + anno['2d_bbox_left'] = np.array([float(x[4]) for x in content]) + anno['2d_bbox_top'] = np.array([float(x[5]) for x in content]) + anno['2d_bbox_right'] = np.array([float(x[6]) for x in content]) + anno['2d_bbox_bottom'] = np.array([float(x[7]) for x in content]) + + anno['3d_bbox_height'] = np.array([float(x[8]) for x in content]) + anno['3d_bbox_width'] = np.array([float(x[9]) for x in content]) + anno['3d_bbox_length'] = np.array([float(x[10]) for x in content]) + anno['3d_bbox_x'] = np.array([float(x[11]) for x in content]) + anno['3d_bbox_y'] = np.array([float(x[12]) for x in content]) + anno['3d_bbox_z'] = np.array([float(x[13]) for x in content]) + anno['3d_bbox_rot_y'] = np.array([float(x[14]) for x in content]) + + return anno + + +def main(_): + convert_kitti_to_tfrecords( + data_dir=FLAGS.data_dir, + output_path=FLAGS.output_path, + classes_to_use=FLAGS.classes_to_use.split(','), + label_map_path=FLAGS.label_map_path, + validation_set_size=FLAGS.validation_set_size) + +if __name__ == '__main__': + tf.app.run() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record_test.py b/workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record_test.py new file mode 100644 index 0000000..606c684 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_kitti_tf_record_test.py @@ -0,0 +1,132 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Test for create_kitti_tf_record.py.""" + +import os + +import numpy as np +import PIL.Image +import six +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import create_kitti_tf_record + + +class CreateKittiTFRecordTest(tf.test.TestCase): + + def _assertProtoEqual(self, proto_field, expectation): + """Helper function to assert if a proto field equals some value. + + Args: + proto_field: The protobuf field to compare. + expectation: The expected value of the protobuf field. + """ + proto_list = [p for p in proto_field] + self.assertListEqual(proto_list, expectation) + + def test_dict_to_tf_example(self): + image_file_name = 'tmp_image.jpg' + image_data = np.random.rand(256, 256, 3) + save_path = os.path.join(self.get_temp_dir(), image_file_name) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + annotations = {} + annotations['2d_bbox_left'] = np.array([64]) + annotations['2d_bbox_top'] = np.array([64]) + annotations['2d_bbox_right'] = np.array([192]) + annotations['2d_bbox_bottom'] = np.array([192]) + annotations['type'] = ['car'] + annotations['truncated'] = np.array([1]) + annotations['alpha'] = np.array([2]) + annotations['3d_bbox_height'] = np.array([10]) + annotations['3d_bbox_width'] = np.array([11]) + annotations['3d_bbox_length'] = np.array([12]) + annotations['3d_bbox_x'] = np.array([13]) + annotations['3d_bbox_y'] = np.array([14]) + annotations['3d_bbox_z'] = np.array([15]) + annotations['3d_bbox_rot_y'] = np.array([4]) + + label_map_dict = { + 'background': 0, + 'car': 1, + } + + example = create_kitti_tf_record.prepare_example( + save_path, + annotations, + label_map_dict) + + self._assertProtoEqual( + example.features.feature['image/height'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/width'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/filename'].bytes_list.value, + [six.b(save_path)]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [six.b(save_path)]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, + [six.b('png')]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/class/text'].bytes_list.value, + [six.b('car')]) + self._assertProtoEqual( + example.features.feature['image/object/class/label'].int64_list.value, + [1]) + self._assertProtoEqual( + example.features.feature['image/object/truncated'].float_list.value, + [1]) + self._assertProtoEqual( + example.features.feature['image/object/alpha'].float_list.value, + [2]) + self._assertProtoEqual(example.features.feature[ + 'image/object/3d_bbox/height'].float_list.value, [10]) + self._assertProtoEqual( + example.features.feature['image/object/3d_bbox/width'].float_list.value, + [11]) + self._assertProtoEqual(example.features.feature[ + 'image/object/3d_bbox/length'].float_list.value, [12]) + self._assertProtoEqual( + example.features.feature['image/object/3d_bbox/x'].float_list.value, + [13]) + self._assertProtoEqual( + example.features.feature['image/object/3d_bbox/y'].float_list.value, + [14]) + self._assertProtoEqual( + example.features.feature['image/object/3d_bbox/z'].float_list.value, + [15]) + self._assertProtoEqual( + example.features.feature['image/object/3d_bbox/rot_y'].float_list.value, + [4]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_oid_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_oid_tf_record.py new file mode 100644 index 0000000..9b35765 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_oid_tf_record.py @@ -0,0 +1,117 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Creates TFRecords of Open Images dataset for object detection. + +Example usage: + python object_detection/dataset_tools/create_oid_tf_record.py \ + --input_box_annotations_csv=/path/to/input/annotations-human-bbox.csv \ + --input_image_label_annotations_csv=/path/to/input/annotations-label.csv \ + --input_images_directory=/path/to/input/image_pixels_directory \ + --input_label_map=/path/to/input/labels_bbox_545.labelmap \ + --output_tf_record_path_prefix=/path/to/output/prefix.tfrecord + +CSVs with bounding box annotations and image metadata (including the image URLs) +can be downloaded from the Open Images GitHub repository: +https://github.com/openimages/dataset + +This script will include every image found in the input_images_directory in the +output TFRecord, even if the image has no corresponding bounding box annotations +in the input_annotations_csv. If input_image_label_annotations_csv is specified, +it will add image-level labels as well. Note that the information of whether a +label is positivelly or negativelly verified is NOT added to tfrecord. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +import contextlib2 +import pandas as pd +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import oid_tfrecord_creation +from object_detection.dataset_tools import tf_record_creation_util +from object_detection.utils import label_map_util + +tf.flags.DEFINE_string('input_box_annotations_csv', None, + 'Path to CSV containing image bounding box annotations') +tf.flags.DEFINE_string('input_images_directory', None, + 'Directory containing the image pixels ' + 'downloaded from the OpenImages GitHub repository.') +tf.flags.DEFINE_string('input_image_label_annotations_csv', None, + 'Path to CSV containing image-level labels annotations') +tf.flags.DEFINE_string('input_label_map', None, 'Path to the label map proto') +tf.flags.DEFINE_string( + 'output_tf_record_path_prefix', None, + 'Path to the output TFRecord. The shard index and the number of shards ' + 'will be appended for each output shard.') +tf.flags.DEFINE_integer('num_shards', 100, 'Number of TFRecord shards') + +FLAGS = tf.flags.FLAGS + + +def main(_): + tf.logging.set_verbosity(tf.logging.INFO) + + required_flags = [ + 'input_box_annotations_csv', 'input_images_directory', 'input_label_map', + 'output_tf_record_path_prefix' + ] + for flag_name in required_flags: + if not getattr(FLAGS, flag_name): + raise ValueError('Flag --{} is required'.format(flag_name)) + + label_map = label_map_util.get_label_map_dict(FLAGS.input_label_map) + all_box_annotations = pd.read_csv(FLAGS.input_box_annotations_csv) + if FLAGS.input_image_label_annotations_csv: + all_label_annotations = pd.read_csv(FLAGS.input_image_label_annotations_csv) + all_label_annotations.rename( + columns={'Confidence': 'ConfidenceImageLabel'}, inplace=True) + else: + all_label_annotations = None + all_images = tf.gfile.Glob( + os.path.join(FLAGS.input_images_directory, '*.jpg')) + all_image_ids = [os.path.splitext(os.path.basename(v))[0] for v in all_images] + all_image_ids = pd.DataFrame({'ImageID': all_image_ids}) + all_annotations = pd.concat( + [all_box_annotations, all_image_ids, all_label_annotations]) + + tf.logging.log(tf.logging.INFO, 'Found %d images...', len(all_image_ids)) + + with contextlib2.ExitStack() as tf_record_close_stack: + output_tfrecords = tf_record_creation_util.open_sharded_output_tfrecords( + tf_record_close_stack, FLAGS.output_tf_record_path_prefix, + FLAGS.num_shards) + + for counter, image_data in enumerate(all_annotations.groupby('ImageID')): + tf.logging.log_every_n(tf.logging.INFO, 'Processed %d images...', 1000, + counter) + + image_id, image_annotations = image_data + # In OID image file names are formed by appending ".jpg" to the image ID. + image_path = os.path.join(FLAGS.input_images_directory, image_id + '.jpg') + with tf.gfile.Open(image_path) as image_file: + encoded_image = image_file.read() + + tf_example = oid_tfrecord_creation.tf_example_from_annotations_data_frame( + image_annotations, label_map, encoded_image) + if tf_example: + shard_idx = int(image_id, 16) % FLAGS.num_shards + output_tfrecords[shard_idx].write(tf_example.SerializeToString()) + + +if __name__ == '__main__': + tf.app.run() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record.py new file mode 100644 index 0000000..8d79a33 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record.py @@ -0,0 +1,185 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +r"""Convert raw PASCAL dataset to TFRecord for object_detection. + +Example usage: + python object_detection/dataset_tools/create_pascal_tf_record.py \ + --data_dir=/home/user/VOCdevkit \ + --year=VOC2012 \ + --output_path=/home/user/pascal.record +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import hashlib +import io +import logging +import os + +from lxml import etree +import PIL.Image +import tensorflow.compat.v1 as tf + +from object_detection.utils import dataset_util +from object_detection.utils import label_map_util + + +flags = tf.app.flags +flags.DEFINE_string('data_dir', '', 'Root directory to raw PASCAL VOC dataset.') +flags.DEFINE_string('set', 'train', 'Convert training set, validation set or ' + 'merged set.') +flags.DEFINE_string('annotations_dir', 'Annotations', + '(Relative) path to annotations directory.') +flags.DEFINE_string('year', 'VOC2007', 'Desired challenge year.') +flags.DEFINE_string('output_path', '', 'Path to output TFRecord') +flags.DEFINE_string('label_map_path', 'data/pascal_label_map.pbtxt', + 'Path to label map proto') +flags.DEFINE_boolean('ignore_difficult_instances', False, 'Whether to ignore ' + 'difficult instances') +FLAGS = flags.FLAGS + +SETS = ['train', 'val', 'trainval', 'test'] +YEARS = ['VOC2007', 'VOC2012', 'merged'] + + +def dict_to_tf_example(data, + dataset_directory, + label_map_dict, + ignore_difficult_instances=False, + image_subdirectory='JPEGImages'): + """Convert XML derived dict to tf.Example proto. + + Notice that this function normalizes the bounding box coordinates provided + by the raw data. + + Args: + data: dict holding PASCAL XML fields for a single image (obtained by + running dataset_util.recursive_parse_xml_to_dict) + dataset_directory: Path to root directory holding PASCAL dataset + label_map_dict: A map from string label names to integers ids. + ignore_difficult_instances: Whether to skip difficult instances in the + dataset (default: False). + image_subdirectory: String specifying subdirectory within the + PASCAL dataset directory holding the actual image data. + + Returns: + example: The converted tf.Example. + + Raises: + ValueError: if the image pointed to by data['filename'] is not a valid JPEG + """ + img_path = os.path.join(data['folder'], image_subdirectory, data['filename']) + full_path = os.path.join(dataset_directory, img_path) + with tf.gfile.GFile(full_path, 'rb') as fid: + encoded_jpg = fid.read() + encoded_jpg_io = io.BytesIO(encoded_jpg) + image = PIL.Image.open(encoded_jpg_io) + if image.format != 'JPEG': + raise ValueError('Image format not JPEG') + key = hashlib.sha256(encoded_jpg).hexdigest() + + width = int(data['size']['width']) + height = int(data['size']['height']) + + xmin = [] + ymin = [] + xmax = [] + ymax = [] + classes = [] + classes_text = [] + truncated = [] + poses = [] + difficult_obj = [] + if 'object' in data: + for obj in data['object']: + difficult = bool(int(obj['difficult'])) + if ignore_difficult_instances and difficult: + continue + + difficult_obj.append(int(difficult)) + + xmin.append(float(obj['bndbox']['xmin']) / width) + ymin.append(float(obj['bndbox']['ymin']) / height) + xmax.append(float(obj['bndbox']['xmax']) / width) + ymax.append(float(obj['bndbox']['ymax']) / height) + classes_text.append(obj['name'].encode('utf8')) + classes.append(label_map_dict[obj['name']]) + truncated.append(int(obj['truncated'])) + poses.append(obj['pose'].encode('utf8')) + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/height': dataset_util.int64_feature(height), + 'image/width': dataset_util.int64_feature(width), + 'image/filename': dataset_util.bytes_feature( + data['filename'].encode('utf8')), + 'image/source_id': dataset_util.bytes_feature( + data['filename'].encode('utf8')), + 'image/key/sha256': dataset_util.bytes_feature(key.encode('utf8')), + 'image/encoded': dataset_util.bytes_feature(encoded_jpg), + 'image/format': dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/object/bbox/xmin': dataset_util.float_list_feature(xmin), + 'image/object/bbox/xmax': dataset_util.float_list_feature(xmax), + 'image/object/bbox/ymin': dataset_util.float_list_feature(ymin), + 'image/object/bbox/ymax': dataset_util.float_list_feature(ymax), + 'image/object/class/text': dataset_util.bytes_list_feature(classes_text), + 'image/object/class/label': dataset_util.int64_list_feature(classes), + 'image/object/difficult': dataset_util.int64_list_feature(difficult_obj), + 'image/object/truncated': dataset_util.int64_list_feature(truncated), + 'image/object/view': dataset_util.bytes_list_feature(poses), + })) + return example + + +def main(_): + if FLAGS.set not in SETS: + raise ValueError('set must be in : {}'.format(SETS)) + if FLAGS.year not in YEARS: + raise ValueError('year must be in : {}'.format(YEARS)) + + data_dir = FLAGS.data_dir + years = ['VOC2007', 'VOC2012'] + if FLAGS.year != 'merged': + years = [FLAGS.year] + + writer = tf.python_io.TFRecordWriter(FLAGS.output_path) + + label_map_dict = label_map_util.get_label_map_dict(FLAGS.label_map_path) + + for year in years: + logging.info('Reading from PASCAL %s dataset.', year) + examples_path = os.path.join(data_dir, year, 'ImageSets', 'Main', + 'aeroplane_' + FLAGS.set + '.txt') + annotations_dir = os.path.join(data_dir, year, FLAGS.annotations_dir) + examples_list = dataset_util.read_examples_list(examples_path) + for idx, example in enumerate(examples_list): + if idx % 100 == 0: + logging.info('On image %d of %d', idx, len(examples_list)) + path = os.path.join(annotations_dir, example + '.xml') + with tf.gfile.GFile(path, 'r') as fid: + xml_str = fid.read() + xml = etree.fromstring(xml_str) + data = dataset_util.recursive_parse_xml_to_dict(xml)['annotation'] + + tf_example = dict_to_tf_example(data, FLAGS.data_dir, label_map_dict, + FLAGS.ignore_difficult_instances) + writer.write(tf_example.SerializeToString()) + + writer.close() + + +if __name__ == '__main__': + tf.app.run() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record_test.py b/workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record_test.py new file mode 100644 index 0000000..c751a13 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_pascal_tf_record_test.py @@ -0,0 +1,121 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Test for create_pascal_tf_record.py.""" + +import os + +import numpy as np +import PIL.Image +import six +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import create_pascal_tf_record + + +class CreatePascalTFRecordTest(tf.test.TestCase): + + def _assertProtoEqual(self, proto_field, expectation): + """Helper function to assert if a proto field equals some value. + + Args: + proto_field: The protobuf field to compare. + expectation: The expected value of the protobuf field. + """ + proto_list = [p for p in proto_field] + self.assertListEqual(proto_list, expectation) + + def test_dict_to_tf_example(self): + image_file_name = 'tmp_image.jpg' + image_data = np.random.rand(256, 256, 3) + save_path = os.path.join(self.get_temp_dir(), image_file_name) + image = PIL.Image.fromarray(image_data, 'RGB') + image.save(save_path) + + data = { + 'folder': '', + 'filename': image_file_name, + 'size': { + 'height': 256, + 'width': 256, + }, + 'object': [ + { + 'difficult': 1, + 'bndbox': { + 'xmin': 64, + 'ymin': 64, + 'xmax': 192, + 'ymax': 192, + }, + 'name': 'person', + 'truncated': 0, + 'pose': '', + }, + ], + } + + label_map_dict = { + 'background': 0, + 'person': 1, + 'notperson': 2, + } + + example = create_pascal_tf_record.dict_to_tf_example( + data, self.get_temp_dir(), label_map_dict, image_subdirectory='') + self._assertProtoEqual( + example.features.feature['image/height'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/width'].int64_list.value, [256]) + self._assertProtoEqual( + example.features.feature['image/filename'].bytes_list.value, + [six.b(image_file_name)]) + self._assertProtoEqual( + example.features.feature['image/source_id'].bytes_list.value, + [six.b(image_file_name)]) + self._assertProtoEqual( + example.features.feature['image/format'].bytes_list.value, + [six.b('jpeg')]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymin'].float_list.value, + [0.25]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/xmax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/bbox/ymax'].float_list.value, + [0.75]) + self._assertProtoEqual( + example.features.feature['image/object/class/text'].bytes_list.value, + [six.b('person')]) + self._assertProtoEqual( + example.features.feature['image/object/class/label'].int64_list.value, + [1]) + self._assertProtoEqual( + example.features.feature['image/object/difficult'].int64_list.value, + [1]) + self._assertProtoEqual( + example.features.feature['image/object/truncated'].int64_list.value, + [0]) + self._assertProtoEqual( + example.features.feature['image/object/view'].bytes_list.value, + [six.b('')]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_pet_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_pet_tf_record.py new file mode 100644 index 0000000..78524b5 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_pet_tf_record.py @@ -0,0 +1,318 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +r"""Convert the Oxford pet dataset to TFRecord for object_detection. + +See: O. M. Parkhi, A. Vedaldi, A. Zisserman, C. V. Jawahar + Cats and Dogs + IEEE Conference on Computer Vision and Pattern Recognition, 2012 + http://www.robots.ox.ac.uk/~vgg/data/pets/ + +Example usage: + python object_detection/dataset_tools/create_pet_tf_record.py \ + --data_dir=/home/user/pet \ + --output_dir=/home/user/pet/output +""" + +import hashlib +import io +import logging +import os +import random +import re + +import contextlib2 +from lxml import etree +import numpy as np +import PIL.Image +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import tf_record_creation_util +from object_detection.utils import dataset_util +from object_detection.utils import label_map_util + +flags = tf.app.flags +flags.DEFINE_string('data_dir', '', 'Root directory to raw pet dataset.') +flags.DEFINE_string('output_dir', '', 'Path to directory to output TFRecords.') +flags.DEFINE_string('label_map_path', 'data/pet_label_map.pbtxt', + 'Path to label map proto') +flags.DEFINE_boolean('faces_only', True, 'If True, generates bounding boxes ' + 'for pet faces. Otherwise generates bounding boxes (as ' + 'well as segmentations for full pet bodies). Note that ' + 'in the latter case, the resulting files are much larger.') +flags.DEFINE_string('mask_type', 'png', 'How to represent instance ' + 'segmentation masks. Options are "png" or "numerical".') +flags.DEFINE_integer('num_shards', 10, 'Number of TFRecord shards') + +FLAGS = flags.FLAGS + + +def get_class_name_from_filename(file_name): + """Gets the class name from a file. + + Args: + file_name: The file name to get the class name from. + ie. "american_pit_bull_terrier_105.jpg" + + Returns: + A string of the class name. + """ + match = re.match(r'([A-Za-z_]+)(_[0-9]+\.jpg)', file_name, re.I) + return match.groups()[0] + + +def dict_to_tf_example(data, + mask_path, + label_map_dict, + image_subdirectory, + ignore_difficult_instances=False, + faces_only=True, + mask_type='png'): + """Convert XML derived dict to tf.Example proto. + + Notice that this function normalizes the bounding box coordinates provided + by the raw data. + + Args: + data: dict holding PASCAL XML fields for a single image (obtained by + running dataset_util.recursive_parse_xml_to_dict) + mask_path: String path to PNG encoded mask. + label_map_dict: A map from string label names to integers ids. + image_subdirectory: String specifying subdirectory within the + Pascal dataset directory holding the actual image data. + ignore_difficult_instances: Whether to skip difficult instances in the + dataset (default: False). + faces_only: If True, generates bounding boxes for pet faces. Otherwise + generates bounding boxes (as well as segmentations for full pet bodies). + mask_type: 'numerical' or 'png'. 'png' is recommended because it leads to + smaller file sizes. + + Returns: + example: The converted tf.Example. + + Raises: + ValueError: if the image pointed to by data['filename'] is not a valid JPEG + """ + img_path = os.path.join(image_subdirectory, data['filename']) + with tf.gfile.GFile(img_path, 'rb') as fid: + encoded_jpg = fid.read() + encoded_jpg_io = io.BytesIO(encoded_jpg) + image = PIL.Image.open(encoded_jpg_io) + if image.format != 'JPEG': + raise ValueError('Image format not JPEG') + key = hashlib.sha256(encoded_jpg).hexdigest() + + with tf.gfile.GFile(mask_path, 'rb') as fid: + encoded_mask_png = fid.read() + encoded_png_io = io.BytesIO(encoded_mask_png) + mask = PIL.Image.open(encoded_png_io) + if mask.format != 'PNG': + raise ValueError('Mask format not PNG') + + mask_np = np.asarray(mask) + nonbackground_indices_x = np.any(mask_np != 2, axis=0) + nonbackground_indices_y = np.any(mask_np != 2, axis=1) + nonzero_x_indices = np.where(nonbackground_indices_x) + nonzero_y_indices = np.where(nonbackground_indices_y) + + width = int(data['size']['width']) + height = int(data['size']['height']) + + xmins = [] + ymins = [] + xmaxs = [] + ymaxs = [] + classes = [] + classes_text = [] + truncated = [] + poses = [] + difficult_obj = [] + masks = [] + if 'object' in data: + for obj in data['object']: + difficult = bool(int(obj['difficult'])) + if ignore_difficult_instances and difficult: + continue + difficult_obj.append(int(difficult)) + + if faces_only: + xmin = float(obj['bndbox']['xmin']) + xmax = float(obj['bndbox']['xmax']) + ymin = float(obj['bndbox']['ymin']) + ymax = float(obj['bndbox']['ymax']) + else: + xmin = float(np.min(nonzero_x_indices)) + xmax = float(np.max(nonzero_x_indices)) + ymin = float(np.min(nonzero_y_indices)) + ymax = float(np.max(nonzero_y_indices)) + + xmins.append(xmin / width) + ymins.append(ymin / height) + xmaxs.append(xmax / width) + ymaxs.append(ymax / height) + class_name = get_class_name_from_filename(data['filename']) + classes_text.append(class_name.encode('utf8')) + classes.append(label_map_dict[class_name]) + truncated.append(int(obj['truncated'])) + poses.append(obj['pose'].encode('utf8')) + if not faces_only: + mask_remapped = (mask_np != 2).astype(np.uint8) + masks.append(mask_remapped) + + feature_dict = { + 'image/height': dataset_util.int64_feature(height), + 'image/width': dataset_util.int64_feature(width), + 'image/filename': dataset_util.bytes_feature( + data['filename'].encode('utf8')), + 'image/source_id': dataset_util.bytes_feature( + data['filename'].encode('utf8')), + 'image/key/sha256': dataset_util.bytes_feature(key.encode('utf8')), + 'image/encoded': dataset_util.bytes_feature(encoded_jpg), + 'image/format': dataset_util.bytes_feature('jpeg'.encode('utf8')), + 'image/object/bbox/xmin': dataset_util.float_list_feature(xmins), + 'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs), + 'image/object/bbox/ymin': dataset_util.float_list_feature(ymins), + 'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs), + 'image/object/class/text': dataset_util.bytes_list_feature(classes_text), + 'image/object/class/label': dataset_util.int64_list_feature(classes), + 'image/object/difficult': dataset_util.int64_list_feature(difficult_obj), + 'image/object/truncated': dataset_util.int64_list_feature(truncated), + 'image/object/view': dataset_util.bytes_list_feature(poses), + } + if not faces_only: + if mask_type == 'numerical': + mask_stack = np.stack(masks).astype(np.float32) + masks_flattened = np.reshape(mask_stack, [-1]) + feature_dict['image/object/mask'] = ( + dataset_util.float_list_feature(masks_flattened.tolist())) + elif mask_type == 'png': + encoded_mask_png_list = [] + for mask in masks: + img = PIL.Image.fromarray(mask) + output = io.BytesIO() + img.save(output, format='PNG') + encoded_mask_png_list.append(output.getvalue()) + feature_dict['image/object/mask'] = ( + dataset_util.bytes_list_feature(encoded_mask_png_list)) + + example = tf.train.Example(features=tf.train.Features(feature=feature_dict)) + return example + + +def create_tf_record(output_filename, + num_shards, + label_map_dict, + annotations_dir, + image_dir, + examples, + faces_only=True, + mask_type='png'): + """Creates a TFRecord file from examples. + + Args: + output_filename: Path to where output file is saved. + num_shards: Number of shards for output file. + label_map_dict: The label map dictionary. + annotations_dir: Directory where annotation files are stored. + image_dir: Directory where image files are stored. + examples: Examples to parse and save to tf record. + faces_only: If True, generates bounding boxes for pet faces. Otherwise + generates bounding boxes (as well as segmentations for full pet bodies). + mask_type: 'numerical' or 'png'. 'png' is recommended because it leads to + smaller file sizes. + """ + with contextlib2.ExitStack() as tf_record_close_stack: + output_tfrecords = tf_record_creation_util.open_sharded_output_tfrecords( + tf_record_close_stack, output_filename, num_shards) + for idx, example in enumerate(examples): + if idx % 100 == 0: + logging.info('On image %d of %d', idx, len(examples)) + xml_path = os.path.join(annotations_dir, 'xmls', example + '.xml') + mask_path = os.path.join(annotations_dir, 'trimaps', example + '.png') + + if not os.path.exists(xml_path): + logging.warning('Could not find %s, ignoring example.', xml_path) + continue + with tf.gfile.GFile(xml_path, 'r') as fid: + xml_str = fid.read() + xml = etree.fromstring(xml_str) + data = dataset_util.recursive_parse_xml_to_dict(xml)['annotation'] + + try: + tf_example = dict_to_tf_example( + data, + mask_path, + label_map_dict, + image_dir, + faces_only=faces_only, + mask_type=mask_type) + if tf_example: + shard_idx = idx % num_shards + output_tfrecords[shard_idx].write(tf_example.SerializeToString()) + except ValueError: + logging.warning('Invalid example: %s, ignoring.', xml_path) + + +# TODO(derekjchow): Add test for pet/PASCAL main files. +def main(_): + data_dir = FLAGS.data_dir + label_map_dict = label_map_util.get_label_map_dict(FLAGS.label_map_path) + + logging.info('Reading from Pet dataset.') + image_dir = os.path.join(data_dir, 'images') + annotations_dir = os.path.join(data_dir, 'annotations') + examples_path = os.path.join(annotations_dir, 'trainval.txt') + examples_list = dataset_util.read_examples_list(examples_path) + + # Test images are not included in the downloaded data set, so we shall perform + # our own split. + random.seed(42) + random.shuffle(examples_list) + num_examples = len(examples_list) + num_train = int(0.7 * num_examples) + train_examples = examples_list[:num_train] + val_examples = examples_list[num_train:] + logging.info('%d training and %d validation examples.', + len(train_examples), len(val_examples)) + + train_output_path = os.path.join(FLAGS.output_dir, 'pet_faces_train.record') + val_output_path = os.path.join(FLAGS.output_dir, 'pet_faces_val.record') + if not FLAGS.faces_only: + train_output_path = os.path.join(FLAGS.output_dir, + 'pets_fullbody_with_masks_train.record') + val_output_path = os.path.join(FLAGS.output_dir, + 'pets_fullbody_with_masks_val.record') + create_tf_record( + train_output_path, + FLAGS.num_shards, + label_map_dict, + annotations_dir, + image_dir, + train_examples, + faces_only=FLAGS.faces_only, + mask_type=FLAGS.mask_type) + create_tf_record( + val_output_path, + FLAGS.num_shards, + label_map_dict, + annotations_dir, + image_dir, + val_examples, + faces_only=FLAGS.faces_only, + mask_type=FLAGS.mask_type) + + +if __name__ == '__main__': + tf.app.run() diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_pycocotools_package.sh b/workspace/virtuallab/object_detection/dataset_tools/create_pycocotools_package.sh new file mode 100644 index 0000000..88ea511 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_pycocotools_package.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +# Script to download pycocotools and make package for CMLE jobs. +# +# usage: +# bash object_detection/dataset_tools/create_pycocotools_package.sh \ +# /tmp/pycocotools +set -e + +if [ -z "$1" ]; then + echo "usage create_pycocotools_package.sh [output dir]" + exit +fi + +# Create the output directory. +OUTPUT_DIR="${1%/}" +SCRATCH_DIR="${OUTPUT_DIR}/raw" +mkdir -p "${OUTPUT_DIR}" +mkdir -p "${SCRATCH_DIR}" + +cd ${SCRATCH_DIR} +git clone https://github.com/cocodataset/cocoapi.git +cd cocoapi/PythonAPI && mv ../common ./ + +sed "s/\.\.\/common/common/g" setup.py > setup.py.updated +cp -f setup.py.updated setup.py +rm setup.py.updated + +sed "s/\.\.\/common/common/g" pycocotools/_mask.pyx > _mask.pyx.updated +cp -f _mask.pyx.updated pycocotools/_mask.pyx +rm _mask.pyx.updated + +sed "s/import matplotlib\.pyplot as plt/import matplotlib;matplotlib\.use\(\'Agg\'\);import matplotlib\.pyplot as plt/g" pycocotools/coco.py > coco.py.updated +cp -f coco.py.updated pycocotools/coco.py +rm coco.py.updated + +cd "${OUTPUT_DIR}" +tar -czf pycocotools-2.0.tar.gz -C "${SCRATCH_DIR}/cocoapi/" PythonAPI/ +rm -rf ${SCRATCH_DIR} diff --git a/workspace/virtuallab/object_detection/dataset_tools/create_tf_record.py b/workspace/virtuallab/object_detection/dataset_tools/create_tf_record.py new file mode 100644 index 0000000..0f0fd4b --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/create_tf_record.py @@ -0,0 +1,106 @@ +""" +Usage: + # From tensorflow/models/ + # Create train data: + python generate_tfrecord.py --csv_input=data/train_labels.csv --output_path=train.record + # Create test data: + python generate_tfrecord.py --csv_input=data/test_labels.csv --output_path=test.record + +python3 research/object_detection/dataset_tools/create_tf_record.py --csv_input=data/test_labels.csv --output_path=test.record --image_dir=images/test +""" +from __future__ import division +from __future__ import print_function +from __future__ import absolute_import + +import os +import io +import pandas as pd +import tensorflow as tf + +from PIL import Image +import sys +sys.path.append('../') +from object_detection.utils import dataset_util +from collections import namedtuple, OrderedDict + +flags = tf.app.flags +flags.DEFINE_string('csv_input', '', 'data/train_labels.csv') +flags.DEFINE_string('output_path', '', 'data/train.record') +flags.DEFINE_string('image_dir', '', 'images/train') +FLAGS = flags.FLAGS + + +# TO-DO replace this with label map +def class_text_to_int(row_label): + if row_label == 'myrobot': + return 1 + if row_label == 'corobot': + return 2 + else: + return 0 + + +def split(df, group): + data = namedtuple('data', ['filename', 'object']) + gb = df.groupby(group) + return [data(filename, gb.get_group(x)) for filename, x in zip(gb.groups.keys(), gb.groups)] + + +def create_tf_example(group, path): + with tf.gfile.GFile(os.path.join(path, '{}'.format(group.filename)), 'rb') as fid: + encoded_jpg = fid.read() + encoded_jpg_io = io.BytesIO(encoded_jpg) + image = Image.open(encoded_jpg_io) + width, height = image.size + + filename = group.filename.encode('utf8') + image_format = b'jpg' + xmins = [] + xmaxs = [] + ymins = [] + ymaxs = [] + classes_text = [] + classes = [] + + for index, row in group.object.iterrows(): + xmins.append(row['xmin'] / width) + xmaxs.append(row['xmax'] / width) + ymins.append(row['ymin'] / height) + ymaxs.append(row['ymax'] / height) + classes_text.append(row['class'].encode('utf8')) + classes.append(class_text_to_int(row['class'])) + + tf_example = tf.train.Example(features=tf.train.Features(feature={ + 'image/height': dataset_util.int64_feature(height), + 'image/width': dataset_util.int64_feature(width), + 'image/filename': dataset_util.bytes_feature(filename), + 'image/source_id': dataset_util.bytes_feature(filename), + 'image/encoded': dataset_util.bytes_feature(encoded_jpg), + 'image/format': dataset_util.bytes_feature(image_format), + 'image/object/bbox/xmin': dataset_util.float_list_feature(xmins), + 'image/object/bbox/xmax': dataset_util.float_list_feature(xmaxs), + 'image/object/bbox/ymin': dataset_util.float_list_feature(ymins), + 'image/object/bbox/ymax': dataset_util.float_list_feature(ymaxs), + 'image/object/class/text': dataset_util.bytes_list_feature(classes_text), + 'image/object/class/label': dataset_util.int64_list_feature(classes), + })) + return tf_example + + +def main(_): + writer = tf.python_io.TFRecordWriter(FLAGS.output_path) + path = os.path.join(FLAGS.image_dir) + examples = pd.read_csv(FLAGS.csv_input) + grouped = split(examples, 'filename') + for group in grouped: + tf_example = create_tf_example(group, path) + writer.write(tf_example.SerializeToString()) + + writer.close() + output_path = os.path.join(os.getcwd(), FLAGS.output_path) + print('Successfully created the TFRecords: {}'.format(output_path)) + + +if __name__ == '__main__': + tf.app.run() + diff --git a/workspace/virtuallab/object_detection/dataset_tools/densepose/UV_symmetry_transforms.mat b/workspace/virtuallab/object_detection/dataset_tools/densepose/UV_symmetry_transforms.mat new file mode 100644 index 0000000000000000000000000000000000000000..2836cac4d6b37a16fbff8ac6efda1d7ecb88a711 GIT binary patch literal 3148672 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ﯓ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

@?-xuIx|;FV>_RbP-_U^!4UPwy%%@>Lf} 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¢|HmySuxWirAtS@WIbm9nptV6mJr%rz@R+&^NU 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_ava.sh b/workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_ava.sh new file mode 100755 index 0000000..723f6a7 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_ava.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# This script downloads the videos for the AVA dataset. There are no arguments. +# Copy this script into the desired parent directory of the ava_vids_raw/ +# directory created in this script to store the raw videos. + +mkdir ava_vids_raw +cd ava_vids_raw + +curl -O s3.amazonaws.com/ava-dataset/annotations/ava_file_names_trainval_v2.1.txt + +echo "Downloading all videos." + +cat "ava_file_names_trainval_v2.1.txt" | while read line +do + curl -O s3.amazonaws.com/ava-dataset/trainval/$line + echo "Downloaded " $line +done + +rm "ava_file_names_trainval_v2.1.txt" +cd .. + +# Trimming causes issues with frame seeking in the python script, so it is best left out. +# If included, need to modify the python script to subtract 900 seconds wheen seeking. + +# echo "Trimming all videos." + +# mkdir ava_vids_trimmed +# for filename in ava_vids_raw/*; do +# ffmpeg -ss 900 -to 1800 -i $filename -c copy ava_vids_trimmed/${filename##*/} +# done diff --git a/workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_mscoco.sh b/workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_mscoco.sh new file mode 100644 index 0000000..843ba86 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/download_and_preprocess_mscoco.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +# Script to download and preprocess the MSCOCO data set for detection. +# +# The outputs of this script are TFRecord files containing serialized +# tf.Example protocol buffers. See create_coco_tf_record.py for details of how +# the tf.Example protocol buffers are constructed and see +# http://cocodataset.org/#overview for an overview of the dataset. +# +# usage: +# bash object_detection/dataset_tools/download_and_preprocess_mscoco.sh \ +# /tmp/mscoco +set -e + +if [ -z "$1" ]; then + echo "usage download_and_preprocess_mscoco.sh [data dir]" + exit +fi + +if [ "$(uname)" == "Darwin" ]; then + UNZIP="tar -xf" +else + UNZIP="unzip -nq" +fi + +# Create the output directories. +OUTPUT_DIR="${1%/}" +SCRATCH_DIR="${OUTPUT_DIR}/raw-data" +mkdir -p "${OUTPUT_DIR}" +mkdir -p "${SCRATCH_DIR}" +CURRENT_DIR=$(pwd) + +# Helper function to download and unpack a .zip file. +function download_and_unzip() { + local BASE_URL=${1} + local FILENAME=${2} + + if [ ! -f ${FILENAME} ]; then + echo "Downloading ${FILENAME} to $(pwd)" + wget -nd -c "${BASE_URL}/${FILENAME}" + else + echo "Skipping download of ${FILENAME}" + fi + echo "Unzipping ${FILENAME}" + ${UNZIP} ${FILENAME} +} + +cd ${SCRATCH_DIR} + +# Download the images. +BASE_IMAGE_URL="http://images.cocodataset.org/zips" + +TRAIN_IMAGE_FILE="train2017.zip" +download_and_unzip ${BASE_IMAGE_URL} ${TRAIN_IMAGE_FILE} +TRAIN_IMAGE_DIR="${SCRATCH_DIR}/train2017" + +VAL_IMAGE_FILE="val2017.zip" +download_and_unzip ${BASE_IMAGE_URL} ${VAL_IMAGE_FILE} +VAL_IMAGE_DIR="${SCRATCH_DIR}/val2017" + +TEST_IMAGE_FILE="test2017.zip" +download_and_unzip ${BASE_IMAGE_URL} ${TEST_IMAGE_FILE} +TEST_IMAGE_DIR="${SCRATCH_DIR}/test2017" + +# Download the annotations. +BASE_INSTANCES_URL="http://images.cocodataset.org/annotations" +INSTANCES_FILE="annotations_trainval2017.zip" +download_and_unzip ${BASE_INSTANCES_URL} ${INSTANCES_FILE} + +TRAIN_ANNOTATIONS_FILE="${SCRATCH_DIR}/annotations/instances_train2017.json" +VAL_ANNOTATIONS_FILE="${SCRATCH_DIR}/annotations/instances_val2017.json" + +# Download the test image info. +BASE_IMAGE_INFO_URL="http://images.cocodataset.org/annotations" +IMAGE_INFO_FILE="image_info_test2017.zip" +download_and_unzip ${BASE_IMAGE_INFO_URL} ${IMAGE_INFO_FILE} + +TESTDEV_ANNOTATIONS_FILE="${SCRATCH_DIR}/annotations/image_info_test-dev2017.json" + +# Build TFRecords of the image data. +cd "${CURRENT_DIR}" +python object_detection/dataset_tools/create_coco_tf_record.py \ + --logtostderr \ + --include_masks \ + --train_image_dir="${TRAIN_IMAGE_DIR}" \ + --val_image_dir="${VAL_IMAGE_DIR}" \ + --test_image_dir="${TEST_IMAGE_DIR}" \ + --train_annotations_file="${TRAIN_ANNOTATIONS_FILE}" \ + --val_annotations_file="${VAL_ANNOTATIONS_FILE}" \ + --testdev_annotations_file="${TESTDEV_ANNOTATIONS_FILE}" \ + --output_dir="${OUTPUT_DIR}" + diff --git a/workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion.py b/workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion.py new file mode 100644 index 0000000..b3fcf14 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion.py @@ -0,0 +1,233 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""An executable to expand image-level labels, boxes and segments. + +The expansion is performed using class hierarchy, provided in JSON file. + +The expected file formats are the following: +- for box and segment files: CSV file is expected to have LabelName field +- for image-level labels: CSV file is expected to have LabelName and Confidence +fields + +Note, that LabelName is the only field used for expansion. + +Example usage: +python models/research/object_detection/dataset_tools/\ +oid_hierarchical_labels_expansion.py \ +--json_hierarchy_file= \ +--input_annotations= \ +--output_annotations= \ +--annotation_type=<1 (for boxes and segments) or 2 (for image-level labels)> +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import copy +import json +from absl import app +from absl import flags +import six + +flags.DEFINE_string( + 'json_hierarchy_file', None, + 'Path to the file containing label hierarchy in JSON format.') +flags.DEFINE_string( + 'input_annotations', None, 'Path to Open Images annotations file' + '(either bounding boxes, segments or image-level labels).') +flags.DEFINE_string('output_annotations', None, 'Path to the output file.') +flags.DEFINE_integer( + 'annotation_type', None, + 'Type of the input annotations: 1 - boxes or segments,' + '2 - image-level labels.' +) + +FLAGS = flags.FLAGS + + +def _update_dict(initial_dict, update): + """Updates dictionary with update content. + + Args: + initial_dict: initial dictionary. + update: updated dictionary. + """ + + for key, value_list in update.items(): + if key in initial_dict: + initial_dict[key].update(value_list) + else: + initial_dict[key] = set(value_list) + + +def _build_plain_hierarchy(hierarchy, skip_root=False): + """Expands tree hierarchy representation to parent-child dictionary. + + Args: + hierarchy: labels hierarchy as JSON file. + skip_root: if true skips root from the processing (done for the case when all + classes under hierarchy are collected under virtual node). + + Returns: + keyed_parent - dictionary of parent - all its children nodes. + keyed_child - dictionary of children - all its parent nodes + children - all children of the current node. + """ + all_children = set([]) + all_keyed_parent = {} + all_keyed_child = {} + if 'Subcategory' in hierarchy: + for node in hierarchy['Subcategory']: + keyed_parent, keyed_child, children = _build_plain_hierarchy(node) + # Update is not done through dict.update() since some children have multi- + # ple parents in the hiearchy. + _update_dict(all_keyed_parent, keyed_parent) + _update_dict(all_keyed_child, keyed_child) + all_children.update(children) + + if not skip_root: + all_keyed_parent[hierarchy['LabelName']] = copy.deepcopy(all_children) + all_children.add(hierarchy['LabelName']) + for child, _ in all_keyed_child.items(): + all_keyed_child[child].add(hierarchy['LabelName']) + all_keyed_child[hierarchy['LabelName']] = set([]) + + return all_keyed_parent, all_keyed_child, all_children + + +class OIDHierarchicalLabelsExpansion(object): + """ Main class to perform labels hierachical expansion.""" + + def __init__(self, hierarchy): + """Constructor. + + Args: + hierarchy: labels hierarchy as JSON object. + """ + + self._hierarchy_keyed_parent, self._hierarchy_keyed_child, _ = ( + _build_plain_hierarchy(hierarchy, skip_root=True)) + + def expand_boxes_or_segments_from_csv(self, csv_row, + labelname_column_index=1): + """Expands a row containing bounding boxes/segments from CSV file. + + Args: + csv_row: a single row of Open Images released groundtruth file. + labelname_column_index: 0-based index of LabelName column in CSV file. + + Returns: + a list of strings (including the initial row) corresponding to the ground + truth expanded to multiple annotation for evaluation with Open Images + Challenge 2018/2019 metrics. + """ + # Row header is expected to be the following for boxes: + # ImageID,LabelName,Confidence,XMin,XMax,YMin,YMax,IsGroupOf + # Row header is expected to be the following for segments: + # ImageID,LabelName,ImageWidth,ImageHeight,XMin,XMax,YMin,YMax, + # IsGroupOf,Mask + split_csv_row = six.ensure_str(csv_row).split(',') + result = [csv_row] + assert split_csv_row[ + labelname_column_index] in self._hierarchy_keyed_child + parent_nodes = self._hierarchy_keyed_child[ + split_csv_row[labelname_column_index]] + for parent_node in parent_nodes: + split_csv_row[labelname_column_index] = parent_node + result.append(','.join(split_csv_row)) + return result + + def expand_labels_from_csv(self, + csv_row, + labelname_column_index=1, + confidence_column_index=2): + """Expands a row containing labels from CSV file. + + Args: + csv_row: a single row of Open Images released groundtruth file. + labelname_column_index: 0-based index of LabelName column in CSV file. + confidence_column_index: 0-based index of Confidence column in CSV file. + + Returns: + a list of strings (including the initial row) corresponding to the ground + truth expanded to multiple annotation for evaluation with Open Images + Challenge 2018/2019 metrics. + """ + # Row header is expected to be exactly: + # ImageID,Source,LabelName,Confidence + split_csv_row = six.ensure_str(csv_row).split(',') + result = [csv_row] + if int(split_csv_row[confidence_column_index]) == 1: + assert split_csv_row[ + labelname_column_index] in self._hierarchy_keyed_child + parent_nodes = self._hierarchy_keyed_child[ + split_csv_row[labelname_column_index]] + for parent_node in parent_nodes: + split_csv_row[labelname_column_index] = parent_node + result.append(','.join(split_csv_row)) + else: + assert split_csv_row[ + labelname_column_index] in self._hierarchy_keyed_parent + child_nodes = self._hierarchy_keyed_parent[ + split_csv_row[labelname_column_index]] + for child_node in child_nodes: + split_csv_row[labelname_column_index] = child_node + result.append(','.join(split_csv_row)) + return result + + +def main(unused_args): + + del unused_args + + with open(FLAGS.json_hierarchy_file) as f: + hierarchy = json.load(f) + expansion_generator = OIDHierarchicalLabelsExpansion(hierarchy) + labels_file = False + if FLAGS.annotation_type == 2: + labels_file = True + elif FLAGS.annotation_type != 1: + print('--annotation_type expected value is 1 or 2.') + return -1 + confidence_column_index = -1 + labelname_column_index = -1 + with open(FLAGS.input_annotations, 'r') as source: + with open(FLAGS.output_annotations, 'w') as target: + header = source.readline() + target.writelines([header]) + column_names = header.strip().split(',') + labelname_column_index = column_names.index('LabelName') + if labels_file: + confidence_column_index = column_names.index('Confidence') + for line in source: + if labels_file: + expanded_lines = expansion_generator.expand_labels_from_csv( + line, labelname_column_index, confidence_column_index) + else: + expanded_lines = ( + expansion_generator.expand_boxes_or_segments_from_csv( + line, labelname_column_index)) + target.writelines(expanded_lines) + + +if __name__ == '__main__': + flags.mark_flag_as_required('json_hierarchy_file') + flags.mark_flag_as_required('input_annotations') + flags.mark_flag_as_required('output_annotations') + flags.mark_flag_as_required('annotation_type') + + app.run(main) diff --git a/workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion_test.py b/workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion_test.py new file mode 100644 index 0000000..ca010c5 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/oid_hierarchical_labels_expansion_test.py @@ -0,0 +1,116 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for the OpenImages label expansion (OIDHierarchicalLabelsExpansion).""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import oid_hierarchical_labels_expansion + + +def create_test_data(): + hierarchy = { + 'LabelName': + 'a', + 'Subcategory': [{ + 'LabelName': 'b' + }, { + 'LabelName': + 'c', + 'Subcategory': [{ + 'LabelName': 'd' + }, { + 'LabelName': 'e' + }, { + 'LabelName': 'f', + 'Subcategory': [{ + 'LabelName': 'd' + },] + }] + }, { + 'LabelName': 'f', + 'Subcategory': [{ + 'LabelName': 'd' + },] + }] + } + bbox_rows = [ + '123,xclick,b,1,0.1,0.2,0.1,0.2,1,1,0,0,0', + '123,xclick,d,1,0.2,0.3,0.1,0.2,1,1,0,0,0' + ] + label_rows = [ + '123,verification,b,0', '123,verification,c,0', '124,verification,d,1' + ] + segm_rows = [ + '123,cc,b,100,100,0.1,0.2,0.1,0.2,0,MASK', + '123,cc,d,100,100,0.2,0.3,0.1,0.2,0,MASK', + ] + return hierarchy, bbox_rows, segm_rows, label_rows + + +class HierarchicalLabelsExpansionTest(tf.test.TestCase): + + def test_bbox_expansion(self): + hierarchy, bbox_rows, _, _ = create_test_data() + expansion_generator = ( + oid_hierarchical_labels_expansion.OIDHierarchicalLabelsExpansion( + hierarchy)) + all_result_rows = [] + for row in bbox_rows: + all_result_rows.extend( + expansion_generator.expand_boxes_or_segments_from_csv(row, 2)) + self.assertItemsEqual([ + '123,xclick,b,1,0.1,0.2,0.1,0.2,1,1,0,0,0', + '123,xclick,d,1,0.2,0.3,0.1,0.2,1,1,0,0,0', + '123,xclick,f,1,0.2,0.3,0.1,0.2,1,1,0,0,0', + '123,xclick,c,1,0.2,0.3,0.1,0.2,1,1,0,0,0' + ], all_result_rows) + + def test_segm_expansion(self): + hierarchy, _, segm_rows, _ = create_test_data() + expansion_generator = ( + oid_hierarchical_labels_expansion.OIDHierarchicalLabelsExpansion( + hierarchy)) + all_result_rows = [] + for row in segm_rows: + all_result_rows.extend( + expansion_generator.expand_boxes_or_segments_from_csv(row, 2)) + self.assertItemsEqual([ + '123,cc,b,100,100,0.1,0.2,0.1,0.2,0,MASK', + '123,cc,d,100,100,0.2,0.3,0.1,0.2,0,MASK', + '123,cc,f,100,100,0.2,0.3,0.1,0.2,0,MASK', + '123,cc,c,100,100,0.2,0.3,0.1,0.2,0,MASK' + ], all_result_rows) + + def test_labels_expansion(self): + hierarchy, _, _, label_rows = create_test_data() + expansion_generator = ( + oid_hierarchical_labels_expansion.OIDHierarchicalLabelsExpansion( + hierarchy)) + all_result_rows = [] + for row in label_rows: + all_result_rows.extend( + expansion_generator.expand_labels_from_csv(row, 2, 3)) + self.assertItemsEqual([ + '123,verification,b,0', '123,verification,c,0', '123,verification,d,0', + '123,verification,f,0', '123,verification,e,0', '124,verification,d,1', + '124,verification,f,1', '124,verification,c,1' + ], all_result_rows) + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation.py b/workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation.py new file mode 100644 index 0000000..0cddbbb --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation.py @@ -0,0 +1,112 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Utilities for creating TFRecords of TF examples for the Open Images dataset. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import six +import tensorflow.compat.v1 as tf + +from object_detection.core import standard_fields +from object_detection.utils import dataset_util + + +def tf_example_from_annotations_data_frame(annotations_data_frame, label_map, + encoded_image): + """Populates a TF Example message with image annotations from a data frame. + + Args: + annotations_data_frame: Data frame containing the annotations for a single + image. + label_map: String to integer label map. + encoded_image: The encoded image string + + Returns: + The populated TF Example, if the label of at least one object is present in + label_map. Otherwise, returns None. + """ + + filtered_data_frame = annotations_data_frame[ + annotations_data_frame.LabelName.isin(label_map)] + filtered_data_frame_boxes = filtered_data_frame[ + ~filtered_data_frame.YMin.isnull()] + filtered_data_frame_labels = filtered_data_frame[ + filtered_data_frame.YMin.isnull()] + image_id = annotations_data_frame.ImageID.iloc[0] + + feature_map = { + standard_fields.TfExampleFields.object_bbox_ymin: + dataset_util.float_list_feature( + filtered_data_frame_boxes.YMin.to_numpy()), + standard_fields.TfExampleFields.object_bbox_xmin: + dataset_util.float_list_feature( + filtered_data_frame_boxes.XMin.to_numpy()), + standard_fields.TfExampleFields.object_bbox_ymax: + dataset_util.float_list_feature( + filtered_data_frame_boxes.YMax.to_numpy()), + standard_fields.TfExampleFields.object_bbox_xmax: + dataset_util.float_list_feature( + filtered_data_frame_boxes.XMax.to_numpy()), + standard_fields.TfExampleFields.object_class_text: + dataset_util.bytes_list_feature([ + six.ensure_binary(label_text) + for label_text in filtered_data_frame_boxes.LabelName.to_numpy() + ]), + standard_fields.TfExampleFields.object_class_label: + dataset_util.int64_list_feature( + filtered_data_frame_boxes.LabelName.map( + lambda x: label_map[x]).to_numpy()), + standard_fields.TfExampleFields.filename: + dataset_util.bytes_feature( + six.ensure_binary('{}.jpg'.format(image_id))), + standard_fields.TfExampleFields.source_id: + dataset_util.bytes_feature(six.ensure_binary(image_id)), + standard_fields.TfExampleFields.image_encoded: + dataset_util.bytes_feature(six.ensure_binary(encoded_image)), + } + + if 'IsGroupOf' in filtered_data_frame.columns: + feature_map[standard_fields.TfExampleFields. + object_group_of] = dataset_util.int64_list_feature( + filtered_data_frame_boxes.IsGroupOf.to_numpy().astype(int)) + if 'IsOccluded' in filtered_data_frame.columns: + feature_map[standard_fields.TfExampleFields. + object_occluded] = dataset_util.int64_list_feature( + filtered_data_frame_boxes.IsOccluded.to_numpy().astype( + int)) + if 'IsTruncated' in filtered_data_frame.columns: + feature_map[standard_fields.TfExampleFields. + object_truncated] = dataset_util.int64_list_feature( + filtered_data_frame_boxes.IsTruncated.to_numpy().astype( + int)) + if 'IsDepiction' in filtered_data_frame.columns: + feature_map[standard_fields.TfExampleFields. + object_depiction] = dataset_util.int64_list_feature( + filtered_data_frame_boxes.IsDepiction.to_numpy().astype( + int)) + + if 'ConfidenceImageLabel' in filtered_data_frame_labels.columns: + feature_map[standard_fields.TfExampleFields. + image_class_label] = dataset_util.int64_list_feature( + filtered_data_frame_labels.LabelName.map( + lambda x: label_map[x]).to_numpy()) + feature_map[standard_fields.TfExampleFields + .image_class_text] = dataset_util.bytes_list_feature([ + six.ensure_binary(label_text) for label_text in + filtered_data_frame_labels.LabelName.to_numpy() + ]), + return tf.train.Example(features=tf.train.Features(feature=feature_map)) diff --git a/workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation_test.py b/workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation_test.py new file mode 100644 index 0000000..b1e945f --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/oid_tfrecord_creation_test.py @@ -0,0 +1,200 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for oid_tfrecord_creation.py.""" + +import pandas as pd +import six +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import oid_tfrecord_creation + + +def create_test_data(): + data = { + 'ImageID': ['i1', 'i1', 'i1', 'i1', 'i1', 'i2', 'i2'], + 'LabelName': ['a', 'a', 'b', 'b', 'c', 'b', 'c'], + 'YMin': [0.3, 0.6, 0.8, 0.1, None, 0.0, 0.0], + 'XMin': [0.1, 0.3, 0.7, 0.0, None, 0.1, 0.1], + 'XMax': [0.2, 0.3, 0.8, 0.5, None, 0.9, 0.9], + 'YMax': [0.3, 0.6, 1, 0.8, None, 0.8, 0.8], + 'IsOccluded': [0, 1, 1, 0, None, 0, 0], + 'IsTruncated': [0, 0, 0, 1, None, 0, 0], + 'IsGroupOf': [0, 0, 0, 0, None, 0, 1], + 'IsDepiction': [1, 0, 0, 0, None, 0, 0], + 'ConfidenceImageLabel': [None, None, None, None, 0, None, None], + } + df = pd.DataFrame(data=data) + label_map = {'a': 0, 'b': 1, 'c': 2} + return label_map, df + + +class TfExampleFromAnnotationsDataFrameTests(tf.test.TestCase): + + def test_simple(self): + label_map, df = create_test_data() + + tf_example = oid_tfrecord_creation.tf_example_from_annotations_data_frame( + df[df.ImageID == 'i1'], label_map, 'encoded_image_test') + self.assertProtoEquals(six.ensure_str(""" + features { + feature { + key: "image/encoded" + value { bytes_list { value: "encoded_image_test" } } } + feature { + key: "image/filename" + value { bytes_list { value: "i1.jpg" } } } + feature { + key: "image/object/bbox/ymin" + value { float_list { value: [0.3, 0.6, 0.8, 0.1] } } } + feature { + key: "image/object/bbox/xmin" + value { float_list { value: [0.1, 0.3, 0.7, 0.0] } } } + feature { + key: "image/object/bbox/ymax" + value { float_list { value: [0.3, 0.6, 1.0, 0.8] } } } + feature { + key: "image/object/bbox/xmax" + value { float_list { value: [0.2, 0.3, 0.8, 0.5] } } } + feature { + key: "image/object/class/label" + value { int64_list { value: [0, 0, 1, 1] } } } + feature { + key: "image/object/class/text" + value { bytes_list { value: ["a", "a", "b", "b"] } } } + feature { + key: "image/source_id" + value { bytes_list { value: "i1" } } } + feature { + key: "image/object/depiction" + value { int64_list { value: [1, 0, 0, 0] } } } + feature { + key: "image/object/group_of" + value { int64_list { value: [0, 0, 0, 0] } } } + feature { + key: "image/object/occluded" + value { int64_list { value: [0, 1, 1, 0] } } } + feature { + key: "image/object/truncated" + value { int64_list { value: [0, 0, 0, 1] } } } + feature { + key: "image/class/label" + value { int64_list { value: [2] } } } + feature { + key: "image/class/text" + value { bytes_list { value: ["c"] } } } } + """), tf_example) + + def test_no_attributes(self): + label_map, df = create_test_data() + + del df['IsDepiction'] + del df['IsGroupOf'] + del df['IsOccluded'] + del df['IsTruncated'] + del df['ConfidenceImageLabel'] + + tf_example = oid_tfrecord_creation.tf_example_from_annotations_data_frame( + df[df.ImageID == 'i2'], label_map, 'encoded_image_test') + self.assertProtoEquals(six.ensure_str(""" + features { + feature { + key: "image/encoded" + value { bytes_list { value: "encoded_image_test" } } } + feature { + key: "image/filename" + value { bytes_list { value: "i2.jpg" } } } + feature { + key: "image/object/bbox/ymin" + value { float_list { value: [0.0, 0.0] } } } + feature { + key: "image/object/bbox/xmin" + value { float_list { value: [0.1, 0.1] } } } + feature { + key: "image/object/bbox/ymax" + value { float_list { value: [0.8, 0.8] } } } + feature { + key: "image/object/bbox/xmax" + value { float_list { value: [0.9, 0.9] } } } + feature { + key: "image/object/class/label" + value { int64_list { value: [1, 2] } } } + feature { + key: "image/object/class/text" + value { bytes_list { value: ["b", "c"] } } } + feature { + key: "image/source_id" + value { bytes_list { value: "i2" } } } } + """), tf_example) + + def test_label_filtering(self): + label_map, df = create_test_data() + + label_map = {'a': 0} + + tf_example = oid_tfrecord_creation.tf_example_from_annotations_data_frame( + df[df.ImageID == 'i1'], label_map, 'encoded_image_test') + self.assertProtoEquals( + six.ensure_str(""" + features { + feature { + key: "image/encoded" + value { bytes_list { value: "encoded_image_test" } } } + feature { + key: "image/filename" + value { bytes_list { value: "i1.jpg" } } } + feature { + key: "image/object/bbox/ymin" + value { float_list { value: [0.3, 0.6] } } } + feature { + key: "image/object/bbox/xmin" + value { float_list { value: [0.1, 0.3] } } } + feature { + key: "image/object/bbox/ymax" + value { float_list { value: [0.3, 0.6] } } } + feature { + key: "image/object/bbox/xmax" + value { float_list { value: [0.2, 0.3] } } } + feature { + key: "image/object/class/label" + value { int64_list { value: [0, 0] } } } + feature { + key: "image/object/class/text" + value { bytes_list { value: ["a", "a"] } } } + feature { + key: "image/source_id" + value { bytes_list { value: "i1" } } } + feature { + key: "image/object/depiction" + value { int64_list { value: [1, 0] } } } + feature { + key: "image/object/group_of" + value { int64_list { value: [0, 0] } } } + feature { + key: "image/object/occluded" + value { int64_list { value: [0, 1] } } } + feature { + key: "image/object/truncated" + value { int64_list { value: [0, 0] } } } + feature { + key: "image/class/label" + value { int64_list { } } } + feature { + key: "image/class/text" + value { bytes_list { } } } } + """), tf_example) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/seq_example_util.py b/workspace/virtuallab/object_detection/dataset_tools/seq_example_util.py new file mode 100644 index 0000000..4080820 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/seq_example_util.py @@ -0,0 +1,282 @@ +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Common utility for object detection tf.train.SequenceExamples.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import tensorflow.compat.v1 as tf + + +def context_float_feature(ndarray): + """Converts a numpy float array to a context float feature. + + Args: + ndarray: A numpy float array. + + Returns: + A context float feature. + """ + feature = tf.train.Feature() + for val in ndarray: + feature.float_list.value.append(val) + return feature + + +def context_int64_feature(ndarray): + """Converts a numpy array to a context int64 feature. + + Args: + ndarray: A numpy int64 array. + + Returns: + A context int64 feature. + """ + feature = tf.train.Feature() + for val in ndarray: + feature.int64_list.value.append(val) + return feature + + +def context_bytes_feature(ndarray): + """Converts a numpy bytes array to a context bytes feature. + + Args: + ndarray: A numpy bytes array. + + Returns: + A context bytes feature. + """ + feature = tf.train.Feature() + for val in ndarray: + if isinstance(val, np.ndarray): + val = val.tolist() + feature.bytes_list.value.append(tf.compat.as_bytes(val)) + return feature + + +def sequence_float_feature(ndarray): + """Converts a numpy float array to a sequence float feature. + + Args: + ndarray: A numpy float array. + + Returns: + A sequence float feature. + """ + feature_list = tf.train.FeatureList() + for row in ndarray: + feature = feature_list.feature.add() + if row.size: + feature.float_list.value[:] = row + return feature_list + + +def sequence_int64_feature(ndarray): + """Converts a numpy int64 array to a sequence int64 feature. + + Args: + ndarray: A numpy int64 array. + + Returns: + A sequence int64 feature. + """ + feature_list = tf.train.FeatureList() + for row in ndarray: + feature = feature_list.feature.add() + if row.size: + feature.int64_list.value[:] = row + return feature_list + + +def sequence_bytes_feature(ndarray): + """Converts a bytes float array to a sequence bytes feature. + + Args: + ndarray: A numpy bytes array. + + Returns: + A sequence bytes feature. + """ + feature_list = tf.train.FeatureList() + for row in ndarray: + if isinstance(row, np.ndarray): + row = row.tolist() + feature = feature_list.feature.add() + if row: + row = [tf.compat.as_bytes(val) for val in row] + feature.bytes_list.value[:] = row + return feature_list + + +def sequence_strings_feature(strings): + new_str_arr = [] + for single_str in strings: + new_str_arr.append(tf.train.Feature( + bytes_list=tf.train.BytesList( + value=[single_str.encode('utf8')]))) + return tf.train.FeatureList(feature=new_str_arr) + + +def boxes_to_box_components(bboxes): + """Converts a list of numpy arrays (boxes) to box components. + + Args: + bboxes: A numpy array of bounding boxes. + + Returns: + Bounding box component lists. + """ + ymin_list = [] + xmin_list = [] + ymax_list = [] + xmax_list = [] + for bbox in bboxes: + if bbox != []: # pylint: disable=g-explicit-bool-comparison + bbox = np.array(bbox).astype(np.float32) + ymin, xmin, ymax, xmax = np.split(bbox, 4, axis=1) + else: + ymin, xmin, ymax, xmax = [], [], [], [] + ymin_list.append(np.reshape(ymin, [-1])) + xmin_list.append(np.reshape(xmin, [-1])) + ymax_list.append(np.reshape(ymax, [-1])) + xmax_list.append(np.reshape(xmax, [-1])) + return ymin_list, xmin_list, ymax_list, xmax_list + + +def make_sequence_example(dataset_name, + video_id, + encoded_images, + image_height, + image_width, + image_format=None, + image_source_ids=None, + timestamps=None, + is_annotated=None, + bboxes=None, + label_strings=None, + detection_bboxes=None, + detection_classes=None, + detection_scores=None, + use_strs_for_source_id=False): + """Constructs tf.SequenceExamples. + + Args: + dataset_name: String with dataset name. + video_id: String with video id. + encoded_images: A [num_frames] list (or numpy array) of encoded image + frames. + image_height: Height of the images. + image_width: Width of the images. + image_format: Format of encoded images. + image_source_ids: (Optional) A [num_frames] list of unique string ids for + each image. + timestamps: (Optional) A [num_frames] list (or numpy array) array with image + timestamps. + is_annotated: (Optional) A [num_frames] list (or numpy array) array + in which each element indicates whether the frame has been annotated + (1) or not (0). + bboxes: (Optional) A list (with num_frames elements) of [num_boxes_i, 4] + numpy float32 arrays holding boxes for each frame. + label_strings: (Optional) A list (with num_frames_elements) of [num_boxes_i] + numpy string arrays holding object string labels for each frame. + detection_bboxes: (Optional) A list (with num_frames elements) of + [num_boxes_i, 4] numpy float32 arrays holding prediction boxes for each + frame. + detection_classes: (Optional) A list (with num_frames_elements) of + [num_boxes_i] numpy int64 arrays holding predicted classes for each frame. + detection_scores: (Optional) A list (with num_frames_elements) of + [num_boxes_i] numpy float32 arrays holding predicted object scores for + each frame. + use_strs_for_source_id: (Optional) Whether to write the source IDs as + strings rather than byte lists of characters. + + Returns: + A tf.train.SequenceExample. + """ + num_frames = len(encoded_images) + image_encoded = np.expand_dims(encoded_images, axis=-1) + if timestamps is None: + timestamps = np.arange(num_frames) + image_timestamps = np.expand_dims(timestamps, axis=-1) + + # Context fields. + context_dict = { + 'example/dataset_name': context_bytes_feature([dataset_name]), + 'clip/start/timestamp': context_int64_feature([image_timestamps[0][0]]), + 'clip/end/timestamp': context_int64_feature([image_timestamps[-1][0]]), + 'clip/frames': context_int64_feature([num_frames]), + 'image/channels': context_int64_feature([3]), + 'image/height': context_int64_feature([image_height]), + 'image/width': context_int64_feature([image_width]), + 'clip/media_id': context_bytes_feature([video_id]) + } + + # Sequence fields. + feature_list = { + 'image/encoded': sequence_bytes_feature(image_encoded), + 'image/timestamp': sequence_int64_feature(image_timestamps), + } + + # Add optional fields. + if image_format is not None: + context_dict['image/format'] = context_bytes_feature([image_format]) + if image_source_ids is not None: + if use_strs_for_source_id: + feature_list['image/source_id'] = sequence_strings_feature( + image_source_ids) + else: + feature_list['image/source_id'] = sequence_bytes_feature(image_source_ids) + if bboxes is not None: + bbox_ymin, bbox_xmin, bbox_ymax, bbox_xmax = boxes_to_box_components(bboxes) + feature_list['region/bbox/xmin'] = sequence_float_feature(bbox_xmin) + feature_list['region/bbox/xmax'] = sequence_float_feature(bbox_xmax) + feature_list['region/bbox/ymin'] = sequence_float_feature(bbox_ymin) + feature_list['region/bbox/ymax'] = sequence_float_feature(bbox_ymax) + if is_annotated is None: + is_annotated = np.ones(num_frames, dtype=np.int64) + is_annotated = np.expand_dims(is_annotated, axis=-1) + feature_list['region/is_annotated'] = sequence_int64_feature(is_annotated) + + if label_strings is not None: + feature_list['region/label/string'] = sequence_bytes_feature( + label_strings) + + if detection_bboxes is not None: + det_bbox_ymin, det_bbox_xmin, det_bbox_ymax, det_bbox_xmax = ( + boxes_to_box_components(detection_bboxes)) + feature_list['predicted/region/bbox/xmin'] = sequence_float_feature( + det_bbox_xmin) + feature_list['predicted/region/bbox/xmax'] = sequence_float_feature( + det_bbox_xmax) + feature_list['predicted/region/bbox/ymin'] = sequence_float_feature( + det_bbox_ymin) + feature_list['predicted/region/bbox/ymax'] = sequence_float_feature( + det_bbox_ymax) + if detection_classes is not None: + feature_list['predicted/region/label/index'] = sequence_int64_feature( + detection_classes) + if detection_scores is not None: + feature_list['predicted/region/label/confidence'] = sequence_float_feature( + detection_scores) + + context = tf.train.Features(feature=context_dict) + feature_lists = tf.train.FeatureLists(feature_list=feature_list) + + sequence_example = tf.train.SequenceExample( + context=context, + feature_lists=feature_lists) + return sequence_example diff --git a/workspace/virtuallab/object_detection/dataset_tools/seq_example_util_test.py b/workspace/virtuallab/object_detection/dataset_tools/seq_example_util_test.py new file mode 100644 index 0000000..5b6c037 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/seq_example_util_test.py @@ -0,0 +1,366 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for object_detection.utils.seq_example_util.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import six +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import seq_example_util +from object_detection.utils import tf_version + + +class SeqExampleUtilTest(tf.test.TestCase): + + def materialize_tensors(self, list_of_tensors): + if tf_version.is_tf2(): + return [tensor.numpy() for tensor in list_of_tensors] + else: + with self.cached_session() as sess: + return sess.run(list_of_tensors) + + def test_make_unlabeled_example(self): + num_frames = 5 + image_height = 100 + image_width = 200 + dataset_name = b'unlabeled_dataset' + video_id = b'video_000' + images = tf.cast(tf.random.uniform( + [num_frames, image_height, image_width, 3], + maxval=256, + dtype=tf.int32), dtype=tf.uint8) + image_source_ids = [str(idx) for idx in range(num_frames)] + images_list = tf.unstack(images, axis=0) + encoded_images_list = [tf.io.encode_jpeg(image) for image in images_list] + encoded_images = self.materialize_tensors(encoded_images_list) + seq_example = seq_example_util.make_sequence_example( + dataset_name=dataset_name, + video_id=video_id, + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + image_format='JPEG', + image_source_ids=image_source_ids) + + context_feature_dict = seq_example.context.feature + self.assertEqual( + dataset_name, + context_feature_dict['example/dataset_name'].bytes_list.value[0]) + self.assertEqual( + 0, + context_feature_dict['clip/start/timestamp'].int64_list.value[0]) + self.assertEqual( + num_frames - 1, + context_feature_dict['clip/end/timestamp'].int64_list.value[0]) + self.assertEqual( + num_frames, + context_feature_dict['clip/frames'].int64_list.value[0]) + self.assertEqual( + 3, + context_feature_dict['image/channels'].int64_list.value[0]) + self.assertEqual( + b'JPEG', + context_feature_dict['image/format'].bytes_list.value[0]) + self.assertEqual( + image_height, + context_feature_dict['image/height'].int64_list.value[0]) + self.assertEqual( + image_width, + context_feature_dict['image/width'].int64_list.value[0]) + self.assertEqual( + video_id, + context_feature_dict['clip/media_id'].bytes_list.value[0]) + + seq_feature_dict = seq_example.feature_lists.feature_list + self.assertLen( + seq_feature_dict['image/encoded'].feature[:], + num_frames) + timestamps = [ + feature.int64_list.value[0] for feature + in seq_feature_dict['image/timestamp'].feature] + self.assertAllEqual(list(range(num_frames)), timestamps) + source_ids = [ + feature.bytes_list.value[0] for feature + in seq_feature_dict['image/source_id'].feature] + self.assertAllEqual( + [six.ensure_binary(str(idx)) for idx in range(num_frames)], + source_ids) + + def test_make_labeled_example(self): + num_frames = 3 + image_height = 100 + image_width = 200 + dataset_name = b'unlabeled_dataset' + video_id = b'video_000' + labels = [b'dog', b'cat', b'wolf'] + images = tf.cast(tf.random.uniform( + [num_frames, image_height, image_width, 3], + maxval=256, + dtype=tf.int32), dtype=tf.uint8) + images_list = tf.unstack(images, axis=0) + encoded_images_list = [tf.io.encode_jpeg(image) for image in images_list] + encoded_images = self.materialize_tensors(encoded_images_list) + timestamps = [100000, 110000, 120000] + is_annotated = [1, 0, 1] + bboxes = [ + np.array([[0., 0., 0., 0.], + [0., 0., 1., 1.]], dtype=np.float32), + np.zeros([0, 4], dtype=np.float32), + np.array([], dtype=np.float32) + ] + label_strings = [ + np.array(labels), + np.array([]), + np.array([]) + ] + + seq_example = seq_example_util.make_sequence_example( + dataset_name=dataset_name, + video_id=video_id, + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + timestamps=timestamps, + is_annotated=is_annotated, + bboxes=bboxes, + label_strings=label_strings) + + context_feature_dict = seq_example.context.feature + self.assertEqual( + dataset_name, + context_feature_dict['example/dataset_name'].bytes_list.value[0]) + self.assertEqual( + timestamps[0], + context_feature_dict['clip/start/timestamp'].int64_list.value[0]) + self.assertEqual( + timestamps[-1], + context_feature_dict['clip/end/timestamp'].int64_list.value[0]) + self.assertEqual( + num_frames, + context_feature_dict['clip/frames'].int64_list.value[0]) + + seq_feature_dict = seq_example.feature_lists.feature_list + self.assertLen( + seq_feature_dict['image/encoded'].feature[:], + num_frames) + actual_timestamps = [ + feature.int64_list.value[0] for feature + in seq_feature_dict['image/timestamp'].feature] + self.assertAllEqual(timestamps, actual_timestamps) + # Frame 0. + self.assertAllEqual( + is_annotated[0], + seq_feature_dict['region/is_annotated'].feature[0].int64_list.value[0]) + self.assertAllClose( + [0., 0.], + seq_feature_dict['region/bbox/ymin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0., 0.], + seq_feature_dict['region/bbox/xmin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0., 1.], + seq_feature_dict['region/bbox/ymax'].feature[0].float_list.value[:]) + self.assertAllClose( + [0., 1.], + seq_feature_dict['region/bbox/xmax'].feature[0].float_list.value[:]) + self.assertAllEqual( + labels, + seq_feature_dict['region/label/string'].feature[0].bytes_list.value[:]) + + # Frame 1. + self.assertAllEqual( + is_annotated[1], + seq_feature_dict['region/is_annotated'].feature[1].int64_list.value[0]) + self.assertAllClose( + [], + seq_feature_dict['region/bbox/ymin'].feature[1].float_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict['region/bbox/xmin'].feature[1].float_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict['region/bbox/ymax'].feature[1].float_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict['region/bbox/xmax'].feature[1].float_list.value[:]) + self.assertAllEqual( + [], + seq_feature_dict['region/label/string'].feature[1].bytes_list.value[:]) + + def test_make_labeled_example_with_predictions(self): + num_frames = 2 + image_height = 100 + image_width = 200 + dataset_name = b'unlabeled_dataset' + video_id = b'video_000' + images = tf.cast(tf.random.uniform( + [num_frames, image_height, image_width, 3], + maxval=256, + dtype=tf.int32), dtype=tf.uint8) + images_list = tf.unstack(images, axis=0) + encoded_images_list = [tf.io.encode_jpeg(image) for image in images_list] + encoded_images = self.materialize_tensors(encoded_images_list) + bboxes = [ + np.array([[0., 0., 0.75, 0.75], + [0., 0., 1., 1.]], dtype=np.float32), + np.array([[0., 0.25, 0.5, 0.75]], dtype=np.float32) + ] + label_strings = [ + np.array(['cat', 'frog']), + np.array(['cat']) + ] + detection_bboxes = [ + np.array([[0., 0., 0.75, 0.75]], dtype=np.float32), + np.zeros([0, 4], dtype=np.float32) + ] + detection_classes = [ + np.array([5], dtype=np.int64), + np.array([], dtype=np.int64) + ] + detection_scores = [ + np.array([0.9], dtype=np.float32), + np.array([], dtype=np.float32) + ] + + seq_example = seq_example_util.make_sequence_example( + dataset_name=dataset_name, + video_id=video_id, + encoded_images=encoded_images, + image_height=image_height, + image_width=image_width, + bboxes=bboxes, + label_strings=label_strings, + detection_bboxes=detection_bboxes, + detection_classes=detection_classes, + detection_scores=detection_scores) + + context_feature_dict = seq_example.context.feature + self.assertEqual( + dataset_name, + context_feature_dict['example/dataset_name'].bytes_list.value[0]) + self.assertEqual( + 0, + context_feature_dict['clip/start/timestamp'].int64_list.value[0]) + self.assertEqual( + 1, + context_feature_dict['clip/end/timestamp'].int64_list.value[0]) + self.assertEqual( + num_frames, + context_feature_dict['clip/frames'].int64_list.value[0]) + + seq_feature_dict = seq_example.feature_lists.feature_list + self.assertLen( + seq_feature_dict['image/encoded'].feature[:], + num_frames) + actual_timestamps = [ + feature.int64_list.value[0] for feature + in seq_feature_dict['image/timestamp'].feature] + self.assertAllEqual([0, 1], actual_timestamps) + # Frame 0. + self.assertAllEqual( + 1, + seq_feature_dict['region/is_annotated'].feature[0].int64_list.value[0]) + self.assertAllClose( + [0., 0.], + seq_feature_dict['region/bbox/ymin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0., 0.], + seq_feature_dict['region/bbox/xmin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.75, 1.], + seq_feature_dict['region/bbox/ymax'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.75, 1.], + seq_feature_dict['region/bbox/xmax'].feature[0].float_list.value[:]) + self.assertAllEqual( + [b'cat', b'frog'], + seq_feature_dict['region/label/string'].feature[0].bytes_list.value[:]) + self.assertAllClose( + [0.], + seq_feature_dict[ + 'predicted/region/bbox/ymin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.], + seq_feature_dict[ + 'predicted/region/bbox/xmin'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.75], + seq_feature_dict[ + 'predicted/region/bbox/ymax'].feature[0].float_list.value[:]) + self.assertAllClose( + [0.75], + seq_feature_dict[ + 'predicted/region/bbox/xmax'].feature[0].float_list.value[:]) + self.assertAllEqual( + [5], + seq_feature_dict[ + 'predicted/region/label/index'].feature[0].int64_list.value[:]) + self.assertAllClose( + [0.9], + seq_feature_dict[ + 'predicted/region/label/confidence'].feature[0].float_list.value[:]) + + # Frame 1. + self.assertAllEqual( + 1, + seq_feature_dict['region/is_annotated'].feature[1].int64_list.value[0]) + self.assertAllClose( + [0.0], + seq_feature_dict['region/bbox/ymin'].feature[1].float_list.value[:]) + self.assertAllClose( + [0.25], + seq_feature_dict['region/bbox/xmin'].feature[1].float_list.value[:]) + self.assertAllClose( + [0.5], + seq_feature_dict['region/bbox/ymax'].feature[1].float_list.value[:]) + self.assertAllClose( + [0.75], + seq_feature_dict['region/bbox/xmax'].feature[1].float_list.value[:]) + self.assertAllEqual( + [b'cat'], + seq_feature_dict['region/label/string'].feature[1].bytes_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict[ + 'predicted/region/bbox/ymin'].feature[1].float_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict[ + 'predicted/region/bbox/xmin'].feature[1].float_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict[ + 'predicted/region/bbox/ymax'].feature[1].float_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict[ + 'predicted/region/bbox/xmax'].feature[1].float_list.value[:]) + self.assertAllEqual( + [], + seq_feature_dict[ + 'predicted/region/label/index'].feature[1].int64_list.value[:]) + self.assertAllClose( + [], + seq_feature_dict[ + 'predicted/region/label/confidence'].feature[1].float_list.value[:]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util.py b/workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util.py new file mode 100644 index 0000000..e54bcbc --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util.py @@ -0,0 +1,48 @@ +# Lint as: python2, python3 +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Utilities for creating TFRecords of TF examples for the Open Images dataset. +""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from six.moves import range +import tensorflow.compat.v1 as tf + + +def open_sharded_output_tfrecords(exit_stack, base_path, num_shards): + """Opens all TFRecord shards for writing and adds them to an exit stack. + + Args: + exit_stack: A context2.ExitStack used to automatically closed the TFRecords + opened in this function. + base_path: The base path for all shards + num_shards: The number of shards + + Returns: + The list of opened TFRecords. Position k in the list corresponds to shard k. + """ + tf_record_output_filenames = [ + '{}-{:05d}-of-{:05d}'.format(base_path, idx, num_shards) + for idx in range(num_shards) + ] + + tfrecords = [ + exit_stack.enter_context(tf.python_io.TFRecordWriter(file_name)) + for file_name in tf_record_output_filenames + ] + + return tfrecords diff --git a/workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util_test.py b/workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util_test.py new file mode 100644 index 0000000..5722c86 --- /dev/null +++ b/workspace/virtuallab/object_detection/dataset_tools/tf_record_creation_util_test.py @@ -0,0 +1,49 @@ +# Lint as: python2, python3 +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for tf_record_creation_util.py.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os +import contextlib2 +import six +from six.moves import range +import tensorflow.compat.v1 as tf + +from object_detection.dataset_tools import tf_record_creation_util + + +class OpenOutputTfrecordsTests(tf.test.TestCase): + + def test_sharded_tfrecord_writes(self): + with contextlib2.ExitStack() as tf_record_close_stack: + output_tfrecords = tf_record_creation_util.open_sharded_output_tfrecords( + tf_record_close_stack, + os.path.join(tf.test.get_temp_dir(), 'test.tfrec'), 10) + for idx in range(10): + output_tfrecords[idx].write(six.ensure_binary('test_{}'.format(idx))) + + for idx in range(10): + tf_record_path = '{}-{:05d}-of-00010'.format( + os.path.join(tf.test.get_temp_dir(), 'test.tfrec'), idx) + records = list(tf.python_io.tf_record_iterator(tf_record_path)) + self.assertAllEqual(records, ['test_{}'.format(idx).encode('utf-8')]) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/dockerfiles/android/Dockerfile b/workspace/virtuallab/object_detection/dockerfiles/android/Dockerfile new file mode 100644 index 0000000..470f669 --- /dev/null +++ b/workspace/virtuallab/object_detection/dockerfiles/android/Dockerfile @@ -0,0 +1,140 @@ +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# #========================================================================== + +# Pull TF nightly-devel docker image +FROM tensorflow/tensorflow:nightly-devel + +# Get the tensorflow models research directory, and move it into tensorflow +# source folder to match recommendation of installation +RUN git clone --depth 1 https://github.com/tensorflow/models.git && \ + mv models /tensorflow/models + + +# Install gcloud and gsutil commands +# https://cloud.google.com/sdk/docs/quickstart-debian-ubuntu +RUN apt-get -y update && apt-get install -y gpg-agent && \ + export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \ + echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ + apt-get update -y && apt-get install google-cloud-sdk -y + + +# Install the Tensorflow Object Detection API from here +# https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md + +# Install object detection api dependencies - use non-interactive mode to set +# default tzdata config during installation. +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get install -y protobuf-compiler python-pil python-lxml python-tk && \ + pip install Cython && \ + pip install contextlib2 && \ + pip install jupyter && \ + pip install matplotlib + +# Install pycocoapi +RUN git clone --depth 1 https://github.com/cocodataset/cocoapi.git && \ + cd cocoapi/PythonAPI && \ + make -j8 && \ + cp -r pycocotools /tensorflow/models/research && \ + cd ../../ && \ + rm -rf cocoapi + +# Get protoc 3.0.0, rather than the old version already in the container +RUN curl -OL "https://github.com/google/protobuf/releases/download/v3.0.0/protoc-3.0.0-linux-x86_64.zip" && \ + unzip protoc-3.0.0-linux-x86_64.zip -d proto3 && \ + mv proto3/bin/* /usr/local/bin && \ + mv proto3/include/* /usr/local/include && \ + rm -rf proto3 protoc-3.0.0-linux-x86_64.zip + +# Run protoc on the object detection repo +RUN cd /tensorflow/models/research && \ + protoc object_detection/protos/*.proto --python_out=. + +# Set the PYTHONPATH to finish installing the API +ENV PYTHONPATH $PYTHONPATH:/tensorflow/models/research:/tensorflow/models/research/slim + + +# Install wget (to make life easier below) and editors (to allow people to edit +# the files inside the container) +RUN apt-get install -y wget vim emacs nano + + +# Grab various data files which are used throughout the demo: dataset, +# pretrained model, and pretrained TensorFlow Lite model. Install these all in +# the same directories as recommended by the blog post. + +# Pets example dataset +RUN mkdir -p /tmp/pet_faces_tfrecord/ && \ + cd /tmp/pet_faces_tfrecord && \ + curl "http://download.tensorflow.org/models/object_detection/pet_faces_tfrecord.tar.gz" | tar xzf - + +# Pretrained model +# This one doesn't need its own directory, since it comes in a folder. +RUN cd /tmp && \ + curl -O "http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_0.75_depth_300x300_coco14_sync_2018_07_03.tar.gz" && \ + tar xzf ssd_mobilenet_v1_0.75_depth_300x300_coco14_sync_2018_07_03.tar.gz && \ + rm ssd_mobilenet_v1_0.75_depth_300x300_coco14_sync_2018_07_03.tar.gz + +# Trained TensorFlow Lite model. This should get replaced by one generated from +# export_tflite_ssd_graph.py when that command is called. +RUN cd /tmp && \ + curl -L -o tflite.zip \ + https://storage.googleapis.com/download.tensorflow.org/models/tflite/frozengraphs_ssd_mobilenet_v1_0.75_quant_pets_2018_06_29.zip && \ + unzip tflite.zip -d tflite && \ + rm tflite.zip + + +# Install Android development tools +# Inspired by the following sources: +# https://github.com/bitrise-docker/android/blob/master/Dockerfile +# https://github.com/reddit/docker-android-build/blob/master/Dockerfile + +# Set environment variables +ENV ANDROID_HOME /opt/android-sdk-linux +ENV ANDROID_NDK_HOME /opt/android-ndk-r14b +ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools + +# Install SDK tools +RUN cd /opt && \ + curl -OL https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && \ + unzip sdk-tools-linux-4333796.zip -d ${ANDROID_HOME} && \ + rm sdk-tools-linux-4333796.zip + +# Accept licenses before installing components, no need to echo y for each component +# License is valid for all the standard components in versions installed from this file +# Non-standard components: MIPS system images, preview versions, GDK (Google Glass) and Android Google TV require separate licenses, not accepted there +RUN yes | sdkmanager --licenses + +# Install platform tools, SDK platform, and other build tools +RUN yes | sdkmanager \ + "tools" \ + "platform-tools" \ + "platforms;android-27" \ + "platforms;android-23" \ + "build-tools;27.0.3" \ + "build-tools;23.0.3" + +# Install Android NDK (r14b) +RUN cd /opt && \ + curl -L -o android-ndk.zip http://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip && \ + unzip -q android-ndk.zip && \ + rm -f android-ndk.zip + +# Configure the build to use the things we just downloaded +RUN cd /tensorflow && \ + printf '\n\n\nn\ny\nn\nn\nn\ny\nn\nn\nn\nn\nn\nn\n\ny\n%s\n\n\n' ${ANDROID_HOME}|./configure + + +WORKDIR /tensorflow diff --git a/workspace/virtuallab/object_detection/dockerfiles/android/README.md b/workspace/virtuallab/object_detection/dockerfiles/android/README.md new file mode 100644 index 0000000..69016cb --- /dev/null +++ b/workspace/virtuallab/object_detection/dockerfiles/android/README.md @@ -0,0 +1,69 @@ +# Dockerfile for the TPU and TensorFlow Lite Object Detection tutorial + +This Docker image automates the setup involved with training +object detection models on Google Cloud and building the Android TensorFlow Lite +demo app. We recommend using this container if you decide to work through our +tutorial on ["Training and serving a real-time mobile object detector in +30 minutes with Cloud TPUs"](https://medium.com/tensorflow/training-and-serving-a-realtime-mobile-object-detector-in-30-minutes-with-cloud-tpus-b78971cf1193), though of course it may be useful even if you would +like to use the Object Detection API outside the context of the tutorial. + +A couple words of warning: + +1. Docker containers do not have persistent storage. This means that any changes + you make to files inside the container will not persist if you restart + the container. When running through the tutorial, + **do not close the container**. +2. To be able to deploy the [Android app]( + https://github.com/tensorflow/examples/tree/master/lite/examples/object_detection/android) + (which you will build at the end of the tutorial), + you will need to kill any instances of `adb` running on the host machine. You + can accomplish this by closing all instances of Android Studio, and then + running `adb kill-server`. + +You can install Docker by following the [instructions here]( +https://docs.docker.com/install/). + +## Running The Container + +From this directory, build the Dockerfile as follows (this takes a while): + +``` +docker build --tag detect-tf . +``` + +Run the container: + +``` +docker run --rm -it --privileged -p 6006:6006 detect-tf +``` + +When running the container, you will find yourself inside the `/tensorflow` +directory, which is the path to the TensorFlow [source +tree](https://github.com/tensorflow/tensorflow). + +## Text Editing + +The tutorial also +requires you to occasionally edit files inside the source tree. +This Docker images comes with `vim`, `nano`, and `emacs` preinstalled for your +convenience. + +## What's In This Container + +This container is derived from the nightly build of TensorFlow, and contains the +sources for TensorFlow at `/tensorflow`, as well as the +[TensorFlow Models](https://github.com/tensorflow/models) which are available at +`/tensorflow/models` (and contain the Object Detection API as a subdirectory +at `/tensorflow/models/research/object_detection`). +The Oxford-IIIT Pets dataset, the COCO pre-trained SSD + MobileNet (v1) +checkpoint, and example +trained model are all available in `/tmp` in their respective folders. + +This container also has the `gsutil` and `gcloud` utilities, the `bazel` build +tool, and all dependencies necessary to use the Object Detection API, and +compile and install the TensorFlow Lite Android demo app. + +At various points throughout the tutorial, you may see references to the +*research directory*. This refers to the `research` folder within the +models repository, located at +`/tensorflow/models/research`. diff --git a/workspace/virtuallab/object_detection/dockerfiles/tf1/Dockerfile b/workspace/virtuallab/object_detection/dockerfiles/tf1/Dockerfile new file mode 100644 index 0000000..9d77523 --- /dev/null +++ b/workspace/virtuallab/object_detection/dockerfiles/tf1/Dockerfile @@ -0,0 +1,41 @@ +FROM tensorflow/tensorflow:1.15.2-gpu-py3 + +ARG DEBIAN_FRONTEND=noninteractive + +# Install apt dependencies +RUN apt-get update && apt-get install -y \ + git \ + gpg-agent \ + python3-cairocffi \ + protobuf-compiler \ + python3-pil \ + python3-lxml \ + python3-tk \ + wget + +# Install gcloud and gsutil commands +# https://cloud.google.com/sdk/docs/quickstart-debian-ubuntu +RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \ + echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ + apt-get update -y && apt-get install google-cloud-sdk -y + +# Add new user to avoid running as root +RUN useradd -ms /bin/bash tensorflow +USER tensorflow +WORKDIR /home/tensorflow + +# Copy this version of of the model garden into the image +COPY --chown=tensorflow . /home/tensorflow/models + +# Compile protobuf configs +RUN (cd /home/tensorflow/models/research/ && protoc object_detection/protos/*.proto --python_out=.) +WORKDIR /home/tensorflow/models/research/ + +RUN cp object_detection/packages/tf1/setup.py ./ +ENV PATH="/home/tensorflow/.local/bin:${PATH}" + +RUN python -m pip install --user -U pip +RUN python -m pip install --user . + +ENV TF_CPP_MIN_LOG_LEVEL 3 diff --git a/workspace/virtuallab/object_detection/dockerfiles/tf1/README.md b/workspace/virtuallab/object_detection/dockerfiles/tf1/README.md new file mode 100644 index 0000000..9e4503c --- /dev/null +++ b/workspace/virtuallab/object_detection/dockerfiles/tf1/README.md @@ -0,0 +1,11 @@ +# TensorFlow Object Detection on Docker + +These instructions are experimental. + +## Building and running: + +```bash +# From the root of the git repository +docker build -f research/object_detection/dockerfiles/tf1/Dockerfile -t od . +docker run -it od +``` diff --git a/workspace/virtuallab/object_detection/dockerfiles/tf2/Dockerfile b/workspace/virtuallab/object_detection/dockerfiles/tf2/Dockerfile new file mode 100644 index 0000000..c4dfc6b --- /dev/null +++ b/workspace/virtuallab/object_detection/dockerfiles/tf2/Dockerfile @@ -0,0 +1,41 @@ +FROM tensorflow/tensorflow:2.2.0-gpu + +ARG DEBIAN_FRONTEND=noninteractive + +# Install apt dependencies +RUN apt-get update && apt-get install -y \ + git \ + gpg-agent \ + python3-cairocffi \ + protobuf-compiler \ + python3-pil \ + python3-lxml \ + python3-tk \ + wget + +# Install gcloud and gsutil commands +# https://cloud.google.com/sdk/docs/quickstart-debian-ubuntu +RUN export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)" && \ + echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list && \ + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - && \ + apt-get update -y && apt-get install google-cloud-sdk -y + +# Add new user to avoid running as root +RUN useradd -ms /bin/bash tensorflow +USER tensorflow +WORKDIR /home/tensorflow + +# Copy this version of of the model garden into the image +COPY --chown=tensorflow . /home/tensorflow/models + +# Compile protobuf configs +RUN (cd /home/tensorflow/models/research/ && protoc object_detection/protos/*.proto --python_out=.) +WORKDIR /home/tensorflow/models/research/ + +RUN cp object_detection/packages/tf2/setup.py ./ +ENV PATH="/home/tensorflow/.local/bin:${PATH}" + +RUN python -m pip install -U pip +RUN python -m pip install . + +ENV TF_CPP_MIN_LOG_LEVEL 3 diff --git a/workspace/virtuallab/object_detection/dockerfiles/tf2/README.md b/workspace/virtuallab/object_detection/dockerfiles/tf2/README.md new file mode 100644 index 0000000..14b5184 --- /dev/null +++ b/workspace/virtuallab/object_detection/dockerfiles/tf2/README.md @@ -0,0 +1,11 @@ +# TensorFlow Object Detection on Docker + +These instructions are experimental. + +## Building and running: + +```bash +# From the root of the git repository +docker build -f research/object_detection/dockerfiles/tf2/Dockerfile -t od . +docker run -it od +``` diff --git a/workspace/virtuallab/object_detection/eval_util.py b/workspace/virtuallab/object_detection/eval_util.py new file mode 100644 index 0000000..0a44be4 --- /dev/null +++ b/workspace/virtuallab/object_detection/eval_util.py @@ -0,0 +1,1206 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Common utility functions for evaluation.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections +import os +import re +import time + +import numpy as np +from six.moves import range +import tensorflow.compat.v1 as tf + +import tf_slim as slim + +from object_detection.core import box_list +from object_detection.core import box_list_ops +from object_detection.core import keypoint_ops +from object_detection.core import standard_fields as fields +from object_detection.metrics import coco_evaluation +from object_detection.metrics import lvis_evaluation +from object_detection.protos import eval_pb2 +from object_detection.utils import label_map_util +from object_detection.utils import object_detection_evaluation +from object_detection.utils import ops +from object_detection.utils import shape_utils +from object_detection.utils import visualization_utils as vis_utils + +EVAL_KEYPOINT_METRIC = 'coco_keypoint_metrics' + +# A dictionary of metric names to classes that implement the metric. The classes +# in the dictionary must implement +# utils.object_detection_evaluation.DetectionEvaluator interface. +EVAL_METRICS_CLASS_DICT = { + 'coco_detection_metrics': + coco_evaluation.CocoDetectionEvaluator, + 'coco_keypoint_metrics': + coco_evaluation.CocoKeypointEvaluator, + 'coco_mask_metrics': + coco_evaluation.CocoMaskEvaluator, + 'coco_panoptic_metrics': + coco_evaluation.CocoPanopticSegmentationEvaluator, + 'lvis_mask_metrics': + lvis_evaluation.LVISMaskEvaluator, + 'oid_challenge_detection_metrics': + object_detection_evaluation.OpenImagesDetectionChallengeEvaluator, + 'oid_challenge_segmentation_metrics': + object_detection_evaluation + .OpenImagesInstanceSegmentationChallengeEvaluator, + 'pascal_voc_detection_metrics': + object_detection_evaluation.PascalDetectionEvaluator, + 'weighted_pascal_voc_detection_metrics': + object_detection_evaluation.WeightedPascalDetectionEvaluator, + 'precision_at_recall_detection_metrics': + object_detection_evaluation.PrecisionAtRecallDetectionEvaluator, + 'pascal_voc_instance_segmentation_metrics': + object_detection_evaluation.PascalInstanceSegmentationEvaluator, + 'weighted_pascal_voc_instance_segmentation_metrics': + object_detection_evaluation.WeightedPascalInstanceSegmentationEvaluator, + 'oid_V2_detection_metrics': + object_detection_evaluation.OpenImagesDetectionEvaluator, +} + +EVAL_DEFAULT_METRIC = 'coco_detection_metrics' + + +def write_metrics(metrics, global_step, summary_dir): + """Write metrics to a summary directory. + + Args: + metrics: A dictionary containing metric names and values. + global_step: Global step at which the metrics are computed. + summary_dir: Directory to write tensorflow summaries to. + """ + tf.logging.info('Writing metrics to tf summary.') + summary_writer = tf.summary.FileWriterCache.get(summary_dir) + for key in sorted(metrics): + summary = tf.Summary(value=[ + tf.Summary.Value(tag=key, simple_value=metrics[key]), + ]) + summary_writer.add_summary(summary, global_step) + tf.logging.info('%s: %f', key, metrics[key]) + tf.logging.info('Metrics written to tf summary.') + + +# TODO(rathodv): Add tests. +def visualize_detection_results(result_dict, + tag, + global_step, + categories, + summary_dir='', + export_dir='', + agnostic_mode=False, + show_groundtruth=False, + groundtruth_box_visualization_color='black', + min_score_thresh=.5, + max_num_predictions=20, + skip_scores=False, + skip_labels=False, + keep_image_id_for_visualization_export=False): + """Visualizes detection results and writes visualizations to image summaries. + + This function visualizes an image with its detected bounding boxes and writes + to image summaries which can be viewed on tensorboard. It optionally also + writes images to a directory. In the case of missing entry in the label map, + unknown class name in the visualization is shown as "N/A". + + Args: + result_dict: a dictionary holding groundtruth and detection + data corresponding to each image being evaluated. The following keys + are required: + 'original_image': a numpy array representing the image with shape + [1, height, width, 3] or [1, height, width, 1] + 'detection_boxes': a numpy array of shape [N, 4] + 'detection_scores': a numpy array of shape [N] + 'detection_classes': a numpy array of shape [N] + The following keys are optional: + 'groundtruth_boxes': a numpy array of shape [N, 4] + 'groundtruth_keypoints': a numpy array of shape [N, num_keypoints, 2] + Detections are assumed to be provided in decreasing order of score and for + display, and we assume that scores are probabilities between 0 and 1. + tag: tensorboard tag (string) to associate with image. + global_step: global step at which the visualization are generated. + categories: a list of dictionaries representing all possible categories. + Each dict in this list has the following keys: + 'id': (required) an integer id uniquely identifying this category + 'name': (required) string representing category name + e.g., 'cat', 'dog', 'pizza' + 'supercategory': (optional) string representing the supercategory + e.g., 'animal', 'vehicle', 'food', etc + summary_dir: the output directory to which the image summaries are written. + export_dir: the output directory to which images are written. If this is + empty (default), then images are not exported. + agnostic_mode: boolean (default: False) controlling whether to evaluate in + class-agnostic mode or not. + show_groundtruth: boolean (default: False) controlling whether to show + groundtruth boxes in addition to detected boxes + groundtruth_box_visualization_color: box color for visualizing groundtruth + boxes + min_score_thresh: minimum score threshold for a box to be visualized + max_num_predictions: maximum number of detections to visualize + skip_scores: whether to skip score when drawing a single detection + skip_labels: whether to skip label when drawing a single detection + keep_image_id_for_visualization_export: whether to keep image identifier in + filename when exported to export_dir + Raises: + ValueError: if result_dict does not contain the expected keys (i.e., + 'original_image', 'detection_boxes', 'detection_scores', + 'detection_classes') + """ + detection_fields = fields.DetectionResultFields + input_fields = fields.InputDataFields + if not set([ + input_fields.original_image, + detection_fields.detection_boxes, + detection_fields.detection_scores, + detection_fields.detection_classes, + ]).issubset(set(result_dict.keys())): + raise ValueError('result_dict does not contain all expected keys.') + if show_groundtruth and input_fields.groundtruth_boxes not in result_dict: + raise ValueError('If show_groundtruth is enabled, result_dict must contain ' + 'groundtruth_boxes.') + tf.logging.info('Creating detection visualizations.') + category_index = label_map_util.create_category_index(categories) + + image = np.squeeze(result_dict[input_fields.original_image], axis=0) + if image.shape[2] == 1: # If one channel image, repeat in RGB. + image = np.tile(image, [1, 1, 3]) + detection_boxes = result_dict[detection_fields.detection_boxes] + detection_scores = result_dict[detection_fields.detection_scores] + detection_classes = np.int32((result_dict[ + detection_fields.detection_classes])) + detection_keypoints = result_dict.get(detection_fields.detection_keypoints) + detection_masks = result_dict.get(detection_fields.detection_masks) + detection_boundaries = result_dict.get(detection_fields.detection_boundaries) + + # Plot groundtruth underneath detections + if show_groundtruth: + groundtruth_boxes = result_dict[input_fields.groundtruth_boxes] + groundtruth_keypoints = result_dict.get(input_fields.groundtruth_keypoints) + vis_utils.visualize_boxes_and_labels_on_image_array( + image=image, + boxes=groundtruth_boxes, + classes=None, + scores=None, + category_index=category_index, + keypoints=groundtruth_keypoints, + use_normalized_coordinates=False, + max_boxes_to_draw=None, + groundtruth_box_visualization_color=groundtruth_box_visualization_color) + vis_utils.visualize_boxes_and_labels_on_image_array( + image, + detection_boxes, + detection_classes, + detection_scores, + category_index, + instance_masks=detection_masks, + instance_boundaries=detection_boundaries, + keypoints=detection_keypoints, + use_normalized_coordinates=False, + max_boxes_to_draw=max_num_predictions, + min_score_thresh=min_score_thresh, + agnostic_mode=agnostic_mode, + skip_scores=skip_scores, + skip_labels=skip_labels) + + if export_dir: + if keep_image_id_for_visualization_export and result_dict[fields. + InputDataFields() + .key]: + export_path = os.path.join(export_dir, 'export-{}-{}.png'.format( + tag, result_dict[fields.InputDataFields().key])) + else: + export_path = os.path.join(export_dir, 'export-{}.png'.format(tag)) + vis_utils.save_image_array_as_png(image, export_path) + + summary = tf.Summary(value=[ + tf.Summary.Value( + tag=tag, + image=tf.Summary.Image( + encoded_image_string=vis_utils.encode_image_array_as_png_str( + image))) + ]) + summary_writer = tf.summary.FileWriterCache.get(summary_dir) + summary_writer.add_summary(summary, global_step) + + tf.logging.info('Detection visualizations written to summary with tag %s.', + tag) + + +def _run_checkpoint_once(tensor_dict, + evaluators=None, + batch_processor=None, + checkpoint_dirs=None, + variables_to_restore=None, + restore_fn=None, + num_batches=1, + master='', + save_graph=False, + save_graph_dir='', + losses_dict=None, + eval_export_path=None, + process_metrics_fn=None): + """Evaluates metrics defined in evaluators and returns summaries. + + This function loads the latest checkpoint in checkpoint_dirs and evaluates + all metrics defined in evaluators. The metrics are processed in batch by the + batch_processor. + + Args: + tensor_dict: a dictionary holding tensors representing a batch of detections + and corresponding groundtruth annotations. + evaluators: a list of object of type DetectionEvaluator to be used for + evaluation. Note that the metric names produced by different evaluators + must be unique. + batch_processor: a function taking four arguments: + 1. tensor_dict: the same tensor_dict that is passed in as the first + argument to this function. + 2. sess: a tensorflow session + 3. batch_index: an integer representing the index of the batch amongst + all batches + By default, batch_processor is None, which defaults to running: + return sess.run(tensor_dict) + To skip an image, it suffices to return an empty dictionary in place of + result_dict. + checkpoint_dirs: list of directories to load into an EnsembleModel. If it + has only one directory, EnsembleModel will not be used -- + a DetectionModel + will be instantiated directly. Not used if restore_fn is set. + variables_to_restore: None, or a dictionary mapping variable names found in + a checkpoint to model variables. The dictionary would normally be + generated by creating a tf.train.ExponentialMovingAverage object and + calling its variables_to_restore() method. Not used if restore_fn is set. + restore_fn: None, or a function that takes a tf.Session object and correctly + restores all necessary variables from the correct checkpoint file. If + None, attempts to restore from the first directory in checkpoint_dirs. + num_batches: the number of batches to use for evaluation. + master: the location of the Tensorflow session. + save_graph: whether or not the Tensorflow graph is stored as a pbtxt file. + save_graph_dir: where to store the Tensorflow graph on disk. If save_graph + is True this must be non-empty. + losses_dict: optional dictionary of scalar detection losses. + eval_export_path: Path for saving a json file that contains the detection + results in json format. + process_metrics_fn: a callback called with evaluation results after each + evaluation is done. It could be used e.g. to back up checkpoints with + best evaluation scores, or to call an external system to update evaluation + results in order to drive best hyper-parameter search. Parameters are: + int checkpoint_number, Dict[str, ObjectDetectionEvalMetrics] metrics, + str checkpoint_file path. + + Returns: + global_step: the count of global steps. + all_evaluator_metrics: A dictionary containing metric names and values. + + Raises: + ValueError: if restore_fn is None and checkpoint_dirs doesn't have at least + one element. + ValueError: if save_graph is True and save_graph_dir is not defined. + """ + if save_graph and not save_graph_dir: + raise ValueError('`save_graph_dir` must be defined.') + sess = tf.Session(master, graph=tf.get_default_graph()) + sess.run(tf.global_variables_initializer()) + sess.run(tf.local_variables_initializer()) + sess.run(tf.tables_initializer()) + checkpoint_file = None + if restore_fn: + restore_fn(sess) + else: + if not checkpoint_dirs: + raise ValueError('`checkpoint_dirs` must have at least one entry.') + checkpoint_file = tf.train.latest_checkpoint(checkpoint_dirs[0]) + saver = tf.train.Saver(variables_to_restore) + saver.restore(sess, checkpoint_file) + + if save_graph: + tf.train.write_graph(sess.graph_def, save_graph_dir, 'eval.pbtxt') + + counters = {'skipped': 0, 'success': 0} + aggregate_result_losses_dict = collections.defaultdict(list) + with slim.queues.QueueRunners(sess): + try: + for batch in range(int(num_batches)): + if (batch + 1) % 100 == 0: + tf.logging.info('Running eval ops batch %d/%d', batch + 1, + num_batches) + if not batch_processor: + try: + if not losses_dict: + losses_dict = {} + result_dict, result_losses_dict = sess.run([tensor_dict, + losses_dict]) + counters['success'] += 1 + except tf.errors.InvalidArgumentError: + tf.logging.info('Skipping image') + counters['skipped'] += 1 + result_dict = {} + else: + result_dict, result_losses_dict = batch_processor( + tensor_dict, sess, batch, counters, losses_dict=losses_dict) + if not result_dict: + continue + for key, value in iter(result_losses_dict.items()): + aggregate_result_losses_dict[key].append(value) + for evaluator in evaluators: + # TODO(b/65130867): Use image_id tensor once we fix the input data + # decoders to return correct image_id. + # TODO(akuznetsa): result_dict contains batches of images, while + # add_single_ground_truth_image_info expects a single image. Fix + if (isinstance(result_dict, dict) and + fields.InputDataFields.key in result_dict and + result_dict[fields.InputDataFields.key]): + image_id = result_dict[fields.InputDataFields.key] + else: + image_id = batch + evaluator.add_single_ground_truth_image_info( + image_id=image_id, groundtruth_dict=result_dict) + evaluator.add_single_detected_image_info( + image_id=image_id, detections_dict=result_dict) + tf.logging.info('Running eval batches done.') + except tf.errors.OutOfRangeError: + tf.logging.info('Done evaluating -- epoch limit reached') + finally: + # When done, ask the threads to stop. + tf.logging.info('# success: %d', counters['success']) + tf.logging.info('# skipped: %d', counters['skipped']) + all_evaluator_metrics = {} + if eval_export_path and eval_export_path is not None: + for evaluator in evaluators: + if (isinstance(evaluator, coco_evaluation.CocoDetectionEvaluator) or + isinstance(evaluator, coco_evaluation.CocoMaskEvaluator)): + tf.logging.info('Started dumping to json file.') + evaluator.dump_detections_to_json_file( + json_output_path=eval_export_path) + tf.logging.info('Finished dumping to json file.') + for evaluator in evaluators: + metrics = evaluator.evaluate() + evaluator.clear() + if any(key in all_evaluator_metrics for key in metrics): + raise ValueError('Metric names between evaluators must not collide.') + all_evaluator_metrics.update(metrics) + global_step = tf.train.global_step(sess, tf.train.get_global_step()) + + for key, value in iter(aggregate_result_losses_dict.items()): + all_evaluator_metrics['Losses/' + key] = np.mean(value) + if process_metrics_fn and checkpoint_file: + m = re.search(r'model.ckpt-(\d+)$', checkpoint_file) + if not m: + tf.logging.error('Failed to parse checkpoint number from: %s', + checkpoint_file) + else: + checkpoint_number = int(m.group(1)) + process_metrics_fn(checkpoint_number, all_evaluator_metrics, + checkpoint_file) + sess.close() + return (global_step, all_evaluator_metrics) + + +# TODO(rathodv): Add tests. +def repeated_checkpoint_run(tensor_dict, + summary_dir, + evaluators, + batch_processor=None, + checkpoint_dirs=None, + variables_to_restore=None, + restore_fn=None, + num_batches=1, + eval_interval_secs=120, + max_number_of_evaluations=None, + max_evaluation_global_step=None, + master='', + save_graph=False, + save_graph_dir='', + losses_dict=None, + eval_export_path=None, + process_metrics_fn=None): + """Periodically evaluates desired tensors using checkpoint_dirs or restore_fn. + + This function repeatedly loads a checkpoint and evaluates a desired + set of tensors (provided by tensor_dict) and hands the resulting numpy + arrays to a function result_processor which can be used to further + process/save/visualize the results. + + Args: + tensor_dict: a dictionary holding tensors representing a batch of detections + and corresponding groundtruth annotations. + summary_dir: a directory to write metrics summaries. + evaluators: a list of object of type DetectionEvaluator to be used for + evaluation. Note that the metric names produced by different evaluators + must be unique. + batch_processor: a function taking three arguments: + 1. tensor_dict: the same tensor_dict that is passed in as the first + argument to this function. + 2. sess: a tensorflow session + 3. batch_index: an integer representing the index of the batch amongst + all batches + By default, batch_processor is None, which defaults to running: + return sess.run(tensor_dict) + checkpoint_dirs: list of directories to load into a DetectionModel or an + EnsembleModel if restore_fn isn't set. Also used to determine when to run + next evaluation. Must have at least one element. + variables_to_restore: None, or a dictionary mapping variable names found in + a checkpoint to model variables. The dictionary would normally be + generated by creating a tf.train.ExponentialMovingAverage object and + calling its variables_to_restore() method. Not used if restore_fn is set. + restore_fn: a function that takes a tf.Session object and correctly restores + all necessary variables from the correct checkpoint file. + num_batches: the number of batches to use for evaluation. + eval_interval_secs: the number of seconds between each evaluation run. + max_number_of_evaluations: the max number of iterations of the evaluation. + If the value is left as None the evaluation continues indefinitely. + max_evaluation_global_step: global step when evaluation stops. + master: the location of the Tensorflow session. + save_graph: whether or not the Tensorflow graph is saved as a pbtxt file. + save_graph_dir: where to save on disk the Tensorflow graph. If store_graph + is True this must be non-empty. + losses_dict: optional dictionary of scalar detection losses. + eval_export_path: Path for saving a json file that contains the detection + results in json format. + process_metrics_fn: a callback called with evaluation results after each + evaluation is done. It could be used e.g. to back up checkpoints with + best evaluation scores, or to call an external system to update evaluation + results in order to drive best hyper-parameter search. Parameters are: + int checkpoint_number, Dict[str, ObjectDetectionEvalMetrics] metrics, + str checkpoint_file path. + + Returns: + metrics: A dictionary containing metric names and values in the latest + evaluation. + + Raises: + ValueError: if max_num_of_evaluations is not None or a positive number. + ValueError: if checkpoint_dirs doesn't have at least one element. + """ + if max_number_of_evaluations and max_number_of_evaluations <= 0: + raise ValueError( + '`max_number_of_evaluations` must be either None or a positive number.') + if max_evaluation_global_step and max_evaluation_global_step <= 0: + raise ValueError( + '`max_evaluation_global_step` must be either None or positive.') + + if not checkpoint_dirs: + raise ValueError('`checkpoint_dirs` must have at least one entry.') + + last_evaluated_model_path = None + number_of_evaluations = 0 + while True: + start = time.time() + tf.logging.info('Starting evaluation at ' + time.strftime( + '%Y-%m-%d-%H:%M:%S', time.gmtime())) + model_path = tf.train.latest_checkpoint(checkpoint_dirs[0]) + if not model_path: + tf.logging.info('No model found in %s. Will try again in %d seconds', + checkpoint_dirs[0], eval_interval_secs) + elif model_path == last_evaluated_model_path: + tf.logging.info('Found already evaluated checkpoint. Will try again in ' + '%d seconds', eval_interval_secs) + else: + last_evaluated_model_path = model_path + global_step, metrics = _run_checkpoint_once( + tensor_dict, + evaluators, + batch_processor, + checkpoint_dirs, + variables_to_restore, + restore_fn, + num_batches, + master, + save_graph, + save_graph_dir, + losses_dict=losses_dict, + eval_export_path=eval_export_path, + process_metrics_fn=process_metrics_fn) + write_metrics(metrics, global_step, summary_dir) + if (max_evaluation_global_step and + global_step >= max_evaluation_global_step): + tf.logging.info('Finished evaluation!') + break + number_of_evaluations += 1 + + if (max_number_of_evaluations and + number_of_evaluations >= max_number_of_evaluations): + tf.logging.info('Finished evaluation!') + break + time_to_next_eval = start + eval_interval_secs - time.time() + if time_to_next_eval > 0: + time.sleep(time_to_next_eval) + + return metrics + + +def _scale_box_to_absolute(args): + boxes, image_shape = args + return box_list_ops.to_absolute_coordinates( + box_list.BoxList(boxes), image_shape[0], image_shape[1]).get() + + +def _resize_detection_masks(arg_tuple): + """Resizes detection masks. + + Args: + arg_tuple: A (detection_boxes, detection_masks, image_shape, pad_shape) + tuple where + detection_boxes is a tf.float32 tensor of size [num_masks, 4] containing + the box corners. Row i contains [ymin, xmin, ymax, xmax] of the box + corresponding to mask i. Note that the box corners are in + normalized coordinates. + detection_masks is a tensor of size + [num_masks, mask_height, mask_width]. + image_shape is a tensor of shape [2] + pad_shape is a tensor of shape [2] --- this is assumed to be greater + than or equal to image_shape along both dimensions and represents a + shape to-be-padded-to. + + Returns: + """ + detection_boxes, detection_masks, image_shape, pad_shape = arg_tuple + detection_masks_reframed = ops.reframe_box_masks_to_image_masks( + detection_masks, detection_boxes, image_shape[0], image_shape[1]) + paddings = tf.concat( + [tf.zeros([3, 1], dtype=tf.int32), + tf.expand_dims( + tf.concat([tf.zeros([1], dtype=tf.int32), + pad_shape-image_shape], axis=0), + 1)], axis=1) + detection_masks_reframed = tf.pad(detection_masks_reframed, paddings) + + # If the masks are currently float, binarize them. Otherwise keep them as + # integers, since they have already been thresholded. + if detection_masks_reframed.dtype == tf.float32: + detection_masks_reframed = tf.greater(detection_masks_reframed, 0.5) + return tf.cast(detection_masks_reframed, tf.uint8) + + +def resize_detection_masks(detection_boxes, detection_masks, + original_image_spatial_shapes): + """Resizes per-box detection masks to be relative to the entire image. + + Note that this function only works when the spatial size of all images in + the batch is the same. If not, this function should be used with batch_size=1. + + Args: + detection_boxes: A [batch_size, num_instances, 4] float tensor containing + bounding boxes. + detection_masks: A [batch_size, num_instances, height, width] float tensor + containing binary instance masks per box. + original_image_spatial_shapes: a [batch_size, 3] shaped int tensor + holding the spatial dimensions of each image in the batch. + Returns: + masks: Masks resized to the spatial extents given by + (original_image_spatial_shapes[0, 0], original_image_spatial_shapes[0, 1]) + """ + # modify original image spatial shapes to be max along each dim + # in evaluator, should have access to original_image_spatial_shape field + # in add_Eval_Dict + max_spatial_shape = tf.reduce_max( + original_image_spatial_shapes, axis=0, keep_dims=True) + tiled_max_spatial_shape = tf.tile( + max_spatial_shape, + multiples=[tf.shape(original_image_spatial_shapes)[0], 1]) + return shape_utils.static_or_dynamic_map_fn( + _resize_detection_masks, + elems=[detection_boxes, + detection_masks, + original_image_spatial_shapes, + tiled_max_spatial_shape], + dtype=tf.uint8) + + +def _resize_groundtruth_masks(args): + """Resizes groundtruth masks to the original image size.""" + mask, true_image_shape, original_image_shape, pad_shape = args + true_height = true_image_shape[0] + true_width = true_image_shape[1] + mask = mask[:, :true_height, :true_width] + mask = tf.expand_dims(mask, 3) + mask = tf.image.resize_images( + mask, + original_image_shape, + method=tf.image.ResizeMethod.NEAREST_NEIGHBOR, + align_corners=True) + + paddings = tf.concat( + [tf.zeros([3, 1], dtype=tf.int32), + tf.expand_dims( + tf.concat([tf.zeros([1], dtype=tf.int32), + pad_shape-original_image_shape], axis=0), + 1)], axis=1) + mask = tf.pad(tf.squeeze(mask, 3), paddings) + return tf.cast(mask, tf.uint8) + + +def _resize_surface_coordinate_masks(args): + detection_boxes, surface_coords, image_shape = args + surface_coords_v, surface_coords_u = tf.unstack(surface_coords, axis=-1) + surface_coords_v_reframed = ops.reframe_box_masks_to_image_masks( + surface_coords_v, detection_boxes, image_shape[0], image_shape[1]) + surface_coords_u_reframed = ops.reframe_box_masks_to_image_masks( + surface_coords_u, detection_boxes, image_shape[0], image_shape[1]) + return tf.stack([surface_coords_v_reframed, surface_coords_u_reframed], + axis=-1) + + +def _scale_keypoint_to_absolute(args): + keypoints, image_shape = args + return keypoint_ops.scale(keypoints, image_shape[0], image_shape[1]) + + +def result_dict_for_single_example(image, + key, + detections, + groundtruth=None, + class_agnostic=False, + scale_to_absolute=False): + """Merges all detection and groundtruth information for a single example. + + Note that evaluation tools require classes that are 1-indexed, and so this + function performs the offset. If `class_agnostic` is True, all output classes + have label 1. + + Args: + image: A single 4D uint8 image tensor of shape [1, H, W, C]. + key: A single string tensor identifying the image. + detections: A dictionary of detections, returned from + DetectionModel.postprocess(). + groundtruth: (Optional) Dictionary of groundtruth items, with fields: + 'groundtruth_boxes': [num_boxes, 4] float32 tensor of boxes, in + normalized coordinates. + 'groundtruth_classes': [num_boxes] int64 tensor of 1-indexed classes. + 'groundtruth_area': [num_boxes] float32 tensor of bbox area. (Optional) + 'groundtruth_is_crowd': [num_boxes] int64 tensor. (Optional) + 'groundtruth_difficult': [num_boxes] int64 tensor. (Optional) + 'groundtruth_group_of': [num_boxes] int64 tensor. (Optional) + 'groundtruth_instance_masks': 3D int64 tensor of instance masks + (Optional). + 'groundtruth_keypoints': [num_boxes, num_keypoints, 2] float32 tensor with + keypoints (Optional). + class_agnostic: Boolean indicating whether the detections are class-agnostic + (i.e. binary). Default False. + scale_to_absolute: Boolean indicating whether boxes and keypoints should be + scaled to absolute coordinates. Note that for IoU based evaluations, it + does not matter whether boxes are expressed in absolute or relative + coordinates. Default False. + + Returns: + A dictionary with: + 'original_image': A [1, H, W, C] uint8 image tensor. + 'key': A string tensor with image identifier. + 'detection_boxes': [max_detections, 4] float32 tensor of boxes, in + normalized or absolute coordinates, depending on the value of + `scale_to_absolute`. + 'detection_scores': [max_detections] float32 tensor of scores. + 'detection_classes': [max_detections] int64 tensor of 1-indexed classes. + 'detection_masks': [max_detections, H, W] float32 tensor of binarized + masks, reframed to full image masks. + 'groundtruth_boxes': [num_boxes, 4] float32 tensor of boxes, in + normalized or absolute coordinates, depending on the value of + `scale_to_absolute`. (Optional) + 'groundtruth_classes': [num_boxes] int64 tensor of 1-indexed classes. + (Optional) + 'groundtruth_area': [num_boxes] float32 tensor of bbox area. (Optional) + 'groundtruth_is_crowd': [num_boxes] int64 tensor. (Optional) + 'groundtruth_difficult': [num_boxes] int64 tensor. (Optional) + 'groundtruth_group_of': [num_boxes] int64 tensor. (Optional) + 'groundtruth_instance_masks': 3D int64 tensor of instance masks + (Optional). + 'groundtruth_keypoints': [num_boxes, num_keypoints, 2] float32 tensor with + keypoints (Optional). + """ + + if groundtruth: + max_gt_boxes = tf.shape( + groundtruth[fields.InputDataFields.groundtruth_boxes])[0] + for gt_key in groundtruth: + # expand groundtruth dict along the batch dimension. + groundtruth[gt_key] = tf.expand_dims(groundtruth[gt_key], 0) + + for detection_key in detections: + detections[detection_key] = tf.expand_dims( + detections[detection_key][0], axis=0) + + batched_output_dict = result_dict_for_batched_example( + image, + tf.expand_dims(key, 0), + detections, + groundtruth, + class_agnostic, + scale_to_absolute, + max_gt_boxes=max_gt_boxes) + + exclude_keys = [ + fields.InputDataFields.original_image, + fields.DetectionResultFields.num_detections, + fields.InputDataFields.num_groundtruth_boxes + ] + + output_dict = { + fields.InputDataFields.original_image: + batched_output_dict[fields.InputDataFields.original_image] + } + + for key in batched_output_dict: + # remove the batch dimension. + if key not in exclude_keys: + output_dict[key] = tf.squeeze(batched_output_dict[key], 0) + return output_dict + + +def result_dict_for_batched_example(images, + keys, + detections, + groundtruth=None, + class_agnostic=False, + scale_to_absolute=False, + original_image_spatial_shapes=None, + true_image_shapes=None, + max_gt_boxes=None): + """Merges all detection and groundtruth information for a single example. + + Note that evaluation tools require classes that are 1-indexed, and so this + function performs the offset. If `class_agnostic` is True, all output classes + have label 1. + The groundtruth coordinates of boxes/keypoints in 'groundtruth' dictionary are + normalized relative to the (potentially padded) input image, while the + coordinates in 'detection' dictionary are normalized relative to the true + image shape. + + Args: + images: A single 4D uint8 image tensor of shape [batch_size, H, W, C]. + keys: A [batch_size] string/int tensor with image identifier. + detections: A dictionary of detections, returned from + DetectionModel.postprocess(). + groundtruth: (Optional) Dictionary of groundtruth items, with fields: + 'groundtruth_boxes': [batch_size, max_number_of_boxes, 4] float32 tensor + of boxes, in normalized coordinates. + 'groundtruth_classes': [batch_size, max_number_of_boxes] int64 tensor of + 1-indexed classes. + 'groundtruth_area': [batch_size, max_number_of_boxes] float32 tensor of + bbox area. (Optional) + 'groundtruth_is_crowd':[batch_size, max_number_of_boxes] int64 + tensor. (Optional) + 'groundtruth_difficult': [batch_size, max_number_of_boxes] int64 + tensor. (Optional) + 'groundtruth_group_of': [batch_size, max_number_of_boxes] int64 + tensor. (Optional) + 'groundtruth_instance_masks': 4D int64 tensor of instance + masks (Optional). + 'groundtruth_keypoints': [batch_size, max_number_of_boxes, num_keypoints, + 2] float32 tensor with keypoints (Optional). + 'groundtruth_keypoint_visibilities': [batch_size, max_number_of_boxes, + num_keypoints] bool tensor with keypoint visibilities (Optional). + 'groundtruth_labeled_classes': [batch_size, num_classes] int64 + tensor of 1-indexed classes. (Optional) + 'groundtruth_dp_num_points': [batch_size, max_number_of_boxes] int32 + tensor. (Optional) + 'groundtruth_dp_part_ids': [batch_size, max_number_of_boxes, + max_sampled_points] int32 tensor. (Optional) + 'groundtruth_dp_surface_coords_list': [batch_size, max_number_of_boxes, + max_sampled_points, 4] float32 tensor. (Optional) + class_agnostic: Boolean indicating whether the detections are class-agnostic + (i.e. binary). Default False. + scale_to_absolute: Boolean indicating whether boxes and keypoints should be + scaled to absolute coordinates. Note that for IoU based evaluations, it + does not matter whether boxes are expressed in absolute or relative + coordinates. Default False. + original_image_spatial_shapes: A 2D int32 tensor of shape [batch_size, 2] + used to resize the image. When set to None, the image size is retained. + true_image_shapes: A 2D int32 tensor of shape [batch_size, 3] + containing the size of the unpadded original_image. + max_gt_boxes: [batch_size] tensor representing the maximum number of + groundtruth boxes to pad. + + Returns: + A dictionary with: + 'original_image': A [batch_size, H, W, C] uint8 image tensor. + 'original_image_spatial_shape': A [batch_size, 2] tensor containing the + original image sizes. + 'true_image_shape': A [batch_size, 3] tensor containing the size of + the unpadded original_image. + 'key': A [batch_size] string tensor with image identifier. + 'detection_boxes': [batch_size, max_detections, 4] float32 tensor of boxes, + in normalized or absolute coordinates, depending on the value of + `scale_to_absolute`. + 'detection_scores': [batch_size, max_detections] float32 tensor of scores. + 'detection_classes': [batch_size, max_detections] int64 tensor of 1-indexed + classes. + 'detection_masks': [batch_size, max_detections, H, W] uint8 tensor of + instance masks, reframed to full image masks. Note that these may be + binarized (e.g. {0, 1}), or may contain 1-indexed part labels. (Optional) + 'detection_keypoints': [batch_size, max_detections, num_keypoints, 2] + float32 tensor containing keypoint coordinates. (Optional) + 'detection_keypoint_scores': [batch_size, max_detections, num_keypoints] + float32 tensor containing keypoint scores. (Optional) + 'detection_surface_coords': [batch_size, max_detection, H, W, 2] float32 + tensor with normalized surface coordinates (e.g. DensePose UV + coordinates). (Optional) + 'num_detections': [batch_size] int64 tensor containing number of valid + detections. + 'groundtruth_boxes': [batch_size, num_boxes, 4] float32 tensor of boxes, in + normalized or absolute coordinates, depending on the value of + `scale_to_absolute`. (Optional) + 'groundtruth_classes': [batch_size, num_boxes] int64 tensor of 1-indexed + classes. (Optional) + 'groundtruth_area': [batch_size, num_boxes] float32 tensor of bbox + area. (Optional) + 'groundtruth_is_crowd': [batch_size, num_boxes] int64 tensor. (Optional) + 'groundtruth_difficult': [batch_size, num_boxes] int64 tensor. (Optional) + 'groundtruth_group_of': [batch_size, num_boxes] int64 tensor. (Optional) + 'groundtruth_instance_masks': 4D int64 tensor of instance masks + (Optional). + 'groundtruth_keypoints': [batch_size, num_boxes, num_keypoints, 2] float32 + tensor with keypoints (Optional). + 'groundtruth_keypoint_visibilities': [batch_size, num_boxes, num_keypoints] + bool tensor with keypoint visibilities (Optional). + 'groundtruth_labeled_classes': [batch_size, num_classes] int64 tensor + of 1-indexed classes. (Optional) + 'num_groundtruth_boxes': [batch_size] tensor containing the maximum number + of groundtruth boxes per image. + + Raises: + ValueError: if original_image_spatial_shape is not 2D int32 tensor of shape + [2]. + ValueError: if true_image_shapes is not 2D int32 tensor of shape + [3]. + """ + label_id_offset = 1 # Applying label id offset (b/63711816) + + input_data_fields = fields.InputDataFields + if original_image_spatial_shapes is None: + original_image_spatial_shapes = tf.tile( + tf.expand_dims(tf.shape(images)[1:3], axis=0), + multiples=[tf.shape(images)[0], 1]) + else: + if (len(original_image_spatial_shapes.shape) != 2 and + original_image_spatial_shapes.shape[1] != 2): + raise ValueError( + '`original_image_spatial_shape` should be a 2D tensor of shape ' + '[batch_size, 2].') + + if true_image_shapes is None: + true_image_shapes = tf.tile( + tf.expand_dims(tf.shape(images)[1:4], axis=0), + multiples=[tf.shape(images)[0], 1]) + else: + if (len(true_image_shapes.shape) != 2 + and true_image_shapes.shape[1] != 3): + raise ValueError('`true_image_shapes` should be a 2D tensor of ' + 'shape [batch_size, 3].') + + output_dict = { + input_data_fields.original_image: + images, + input_data_fields.key: + keys, + input_data_fields.original_image_spatial_shape: ( + original_image_spatial_shapes), + input_data_fields.true_image_shape: + true_image_shapes + } + + detection_fields = fields.DetectionResultFields + detection_boxes = detections[detection_fields.detection_boxes] + detection_scores = detections[detection_fields.detection_scores] + num_detections = tf.cast(detections[detection_fields.num_detections], + dtype=tf.int32) + + if class_agnostic: + detection_classes = tf.ones_like(detection_scores, dtype=tf.int64) + else: + detection_classes = ( + tf.to_int64(detections[detection_fields.detection_classes]) + + label_id_offset) + + if scale_to_absolute: + output_dict[detection_fields.detection_boxes] = ( + shape_utils.static_or_dynamic_map_fn( + _scale_box_to_absolute, + elems=[detection_boxes, original_image_spatial_shapes], + dtype=tf.float32)) + else: + output_dict[detection_fields.detection_boxes] = detection_boxes + output_dict[detection_fields.detection_classes] = detection_classes + output_dict[detection_fields.detection_scores] = detection_scores + output_dict[detection_fields.num_detections] = num_detections + + if detection_fields.detection_masks in detections: + detection_masks = detections[detection_fields.detection_masks] + output_dict[detection_fields.detection_masks] = resize_detection_masks( + detection_boxes, detection_masks, original_image_spatial_shapes) + + if detection_fields.detection_surface_coords in detections: + detection_surface_coords = detections[ + detection_fields.detection_surface_coords] + output_dict[detection_fields.detection_surface_coords] = ( + shape_utils.static_or_dynamic_map_fn( + _resize_surface_coordinate_masks, + elems=[detection_boxes, detection_surface_coords, + original_image_spatial_shapes], + dtype=tf.float32)) + + if detection_fields.detection_keypoints in detections: + detection_keypoints = detections[detection_fields.detection_keypoints] + output_dict[detection_fields.detection_keypoints] = detection_keypoints + if scale_to_absolute: + output_dict[detection_fields.detection_keypoints] = ( + shape_utils.static_or_dynamic_map_fn( + _scale_keypoint_to_absolute, + elems=[detection_keypoints, original_image_spatial_shapes], + dtype=tf.float32)) + if detection_fields.detection_keypoint_scores in detections: + output_dict[detection_fields.detection_keypoint_scores] = detections[ + detection_fields.detection_keypoint_scores] + else: + output_dict[detection_fields.detection_keypoint_scores] = tf.ones_like( + detections[detection_fields.detection_keypoints][:, :, :, 0]) + + if groundtruth: + if max_gt_boxes is None: + if input_data_fields.num_groundtruth_boxes in groundtruth: + max_gt_boxes = groundtruth[input_data_fields.num_groundtruth_boxes] + else: + raise ValueError( + 'max_gt_boxes must be provided when processing batched examples.') + + if input_data_fields.groundtruth_instance_masks in groundtruth: + masks = groundtruth[input_data_fields.groundtruth_instance_masks] + max_spatial_shape = tf.reduce_max( + original_image_spatial_shapes, axis=0, keep_dims=True) + tiled_max_spatial_shape = tf.tile( + max_spatial_shape, + multiples=[tf.shape(original_image_spatial_shapes)[0], 1]) + groundtruth[input_data_fields.groundtruth_instance_masks] = ( + shape_utils.static_or_dynamic_map_fn( + _resize_groundtruth_masks, + elems=[masks, true_image_shapes, + original_image_spatial_shapes, + tiled_max_spatial_shape], + dtype=tf.uint8)) + + output_dict.update(groundtruth) + + image_shape = tf.cast(tf.shape(images), tf.float32) + image_height, image_width = image_shape[1], image_shape[2] + + def _scale_box_to_normalized_true_image(args): + """Scale the box coordinates to be relative to the true image shape.""" + boxes, true_image_shape = args + true_image_shape = tf.cast(true_image_shape, tf.float32) + true_height, true_width = true_image_shape[0], true_image_shape[1] + normalized_window = tf.stack([0.0, 0.0, true_height / image_height, + true_width / image_width]) + return box_list_ops.change_coordinate_frame( + box_list.BoxList(boxes), normalized_window).get() + + groundtruth_boxes = groundtruth[input_data_fields.groundtruth_boxes] + groundtruth_boxes = shape_utils.static_or_dynamic_map_fn( + _scale_box_to_normalized_true_image, + elems=[groundtruth_boxes, true_image_shapes], dtype=tf.float32) + output_dict[input_data_fields.groundtruth_boxes] = groundtruth_boxes + + if input_data_fields.groundtruth_keypoints in groundtruth: + # If groundtruth_keypoints is in the groundtruth dictionary. Update the + # coordinates to conform with the true image shape. + def _scale_keypoints_to_normalized_true_image(args): + """Scale the box coordinates to be relative to the true image shape.""" + keypoints, true_image_shape = args + true_image_shape = tf.cast(true_image_shape, tf.float32) + true_height, true_width = true_image_shape[0], true_image_shape[1] + normalized_window = tf.stack( + [0.0, 0.0, true_height / image_height, true_width / image_width]) + return keypoint_ops.change_coordinate_frame(keypoints, + normalized_window) + + groundtruth_keypoints = groundtruth[ + input_data_fields.groundtruth_keypoints] + groundtruth_keypoints = shape_utils.static_or_dynamic_map_fn( + _scale_keypoints_to_normalized_true_image, + elems=[groundtruth_keypoints, true_image_shapes], + dtype=tf.float32) + output_dict[ + input_data_fields.groundtruth_keypoints] = groundtruth_keypoints + + if scale_to_absolute: + groundtruth_boxes = output_dict[input_data_fields.groundtruth_boxes] + output_dict[input_data_fields.groundtruth_boxes] = ( + shape_utils.static_or_dynamic_map_fn( + _scale_box_to_absolute, + elems=[groundtruth_boxes, original_image_spatial_shapes], + dtype=tf.float32)) + if input_data_fields.groundtruth_keypoints in groundtruth: + groundtruth_keypoints = output_dict[ + input_data_fields.groundtruth_keypoints] + output_dict[input_data_fields.groundtruth_keypoints] = ( + shape_utils.static_or_dynamic_map_fn( + _scale_keypoint_to_absolute, + elems=[groundtruth_keypoints, original_image_spatial_shapes], + dtype=tf.float32)) + + # For class-agnostic models, groundtruth classes all become 1. + if class_agnostic: + groundtruth_classes = groundtruth[input_data_fields.groundtruth_classes] + groundtruth_classes = tf.ones_like(groundtruth_classes, dtype=tf.int64) + output_dict[input_data_fields.groundtruth_classes] = groundtruth_classes + + output_dict[input_data_fields.num_groundtruth_boxes] = max_gt_boxes + + return output_dict + + +def get_evaluators(eval_config, categories, evaluator_options=None): + """Returns the evaluator class according to eval_config, valid for categories. + + Args: + eval_config: An `eval_pb2.EvalConfig`. + categories: A list of dicts, each of which has the following keys - + 'id': (required) an integer id uniquely identifying this category. + 'name': (required) string representing category name e.g., 'cat', 'dog'. + 'keypoints': (optional) dict mapping this category's keypoints to unique + ids. + evaluator_options: A dictionary of metric names (see + EVAL_METRICS_CLASS_DICT) to `DetectionEvaluator` initialization + keyword arguments. For example: + evalator_options = { + 'coco_detection_metrics': {'include_metrics_per_category': True} + } + + Returns: + An list of instances of DetectionEvaluator. + + Raises: + ValueError: if metric is not in the metric class dictionary. + """ + evaluator_options = evaluator_options or {} + eval_metric_fn_keys = eval_config.metrics_set + if not eval_metric_fn_keys: + eval_metric_fn_keys = [EVAL_DEFAULT_METRIC] + evaluators_list = [] + for eval_metric_fn_key in eval_metric_fn_keys: + if eval_metric_fn_key not in EVAL_METRICS_CLASS_DICT: + raise ValueError('Metric not found: {}'.format(eval_metric_fn_key)) + kwargs_dict = (evaluator_options[eval_metric_fn_key] if eval_metric_fn_key + in evaluator_options else {}) + evaluators_list.append(EVAL_METRICS_CLASS_DICT[eval_metric_fn_key]( + categories, + **kwargs_dict)) + + if isinstance(eval_config, eval_pb2.EvalConfig): + parameterized_metrics = eval_config.parameterized_metric + for parameterized_metric in parameterized_metrics: + assert parameterized_metric.HasField('parameterized_metric') + if parameterized_metric.WhichOneof( + 'parameterized_metric') == EVAL_KEYPOINT_METRIC: + keypoint_metrics = parameterized_metric.coco_keypoint_metrics + # Create category to keypoints mapping dict. + category_keypoints = {} + class_label = keypoint_metrics.class_label + category = None + for cat in categories: + if cat['name'] == class_label: + category = cat + break + if not category: + continue + keypoints_for_this_class = category['keypoints'] + category_keypoints = [{ + 'id': keypoints_for_this_class[kp_name], 'name': kp_name + } for kp_name in keypoints_for_this_class] + # Create keypoint evaluator for this category. + evaluators_list.append(EVAL_METRICS_CLASS_DICT[EVAL_KEYPOINT_METRIC]( + category['id'], category_keypoints, class_label, + keypoint_metrics.keypoint_label_to_sigmas)) + return evaluators_list + + +def get_eval_metric_ops_for_evaluators(eval_config, + categories, + eval_dict): + """Returns eval metrics ops to use with `tf.estimator.EstimatorSpec`. + + Args: + eval_config: An `eval_pb2.EvalConfig`. + categories: A list of dicts, each of which has the following keys - + 'id': (required) an integer id uniquely identifying this category. + 'name': (required) string representing category name e.g., 'cat', 'dog'. + eval_dict: An evaluation dictionary, returned from + result_dict_for_single_example(). + + Returns: + A dictionary of metric names to tuple of value_op and update_op that can be + used as eval metric ops in tf.EstimatorSpec. + """ + eval_metric_ops = {} + evaluator_options = evaluator_options_from_eval_config(eval_config) + evaluators_list = get_evaluators(eval_config, categories, evaluator_options) + for evaluator in evaluators_list: + eval_metric_ops.update(evaluator.get_estimator_eval_metric_ops( + eval_dict)) + return eval_metric_ops + + +def evaluator_options_from_eval_config(eval_config): + """Produces a dictionary of evaluation options for each eval metric. + + Args: + eval_config: An `eval_pb2.EvalConfig`. + + Returns: + evaluator_options: A dictionary of metric names (see + EVAL_METRICS_CLASS_DICT) to `DetectionEvaluator` initialization + keyword arguments. For example: + evalator_options = { + 'coco_detection_metrics': {'include_metrics_per_category': True} + } + """ + eval_metric_fn_keys = eval_config.metrics_set + evaluator_options = {} + for eval_metric_fn_key in eval_metric_fn_keys: + if eval_metric_fn_key in ( + 'coco_detection_metrics', 'coco_mask_metrics', 'lvis_mask_metrics'): + evaluator_options[eval_metric_fn_key] = { + 'include_metrics_per_category': ( + eval_config.include_metrics_per_category) + } + # For coco detection eval, if the eval_config proto contains the + # "skip_predictions_for_unlabeled_class" field, include this field in + # evaluator_options. + if eval_metric_fn_key == 'coco_detection_metrics' and hasattr( + eval_config, 'skip_predictions_for_unlabeled_class'): + evaluator_options[eval_metric_fn_key].update({ + 'skip_predictions_for_unlabeled_class': + (eval_config.skip_predictions_for_unlabeled_class) + }) + for super_category in eval_config.super_categories: + if 'super_categories' not in evaluator_options[eval_metric_fn_key]: + evaluator_options[eval_metric_fn_key]['super_categories'] = {} + key = super_category + value = eval_config.super_categories[key].split(',') + evaluator_options[eval_metric_fn_key]['super_categories'][key] = value + + elif eval_metric_fn_key == 'precision_at_recall_detection_metrics': + evaluator_options[eval_metric_fn_key] = { + 'recall_lower_bound': (eval_config.recall_lower_bound), + 'recall_upper_bound': (eval_config.recall_upper_bound) + } + return evaluator_options + + +def has_densepose(eval_dict): + return (fields.DetectionResultFields.detection_masks in eval_dict and + fields.DetectionResultFields.detection_surface_coords in eval_dict) diff --git a/workspace/virtuallab/object_detection/eval_util_test.py b/workspace/virtuallab/object_detection/eval_util_test.py new file mode 100644 index 0000000..a39a5ff --- /dev/null +++ b/workspace/virtuallab/object_detection/eval_util_test.py @@ -0,0 +1,461 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for eval_util.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import unittest +from absl.testing import parameterized + +import numpy as np +import six +from six.moves import range +import tensorflow.compat.v1 as tf +from google.protobuf import text_format + +from object_detection import eval_util +from object_detection.core import standard_fields as fields +from object_detection.metrics import coco_evaluation +from object_detection.protos import eval_pb2 +from object_detection.utils import test_case +from object_detection.utils import tf_version + + +class EvalUtilTest(test_case.TestCase, parameterized.TestCase): + + def _get_categories_list(self): + return [{'id': 1, 'name': 'person'}, + {'id': 2, 'name': 'dog'}, + {'id': 3, 'name': 'cat'}] + + def _get_categories_list_with_keypoints(self): + return [{ + 'id': 1, + 'name': 'person', + 'keypoints': { + 'left_eye': 0, + 'right_eye': 3 + } + }, { + 'id': 2, + 'name': 'dog', + 'keypoints': { + 'tail_start': 1, + 'mouth': 2 + } + }, { + 'id': 3, + 'name': 'cat' + }] + + def _make_evaluation_dict(self, + resized_groundtruth_masks=False, + batch_size=1, + max_gt_boxes=None, + scale_to_absolute=False): + input_data_fields = fields.InputDataFields + detection_fields = fields.DetectionResultFields + + image = tf.zeros(shape=[batch_size, 20, 20, 3], dtype=tf.uint8) + if batch_size == 1: + key = tf.constant('image1') + else: + key = tf.constant([str(i) for i in range(batch_size)]) + detection_boxes = tf.tile(tf.constant([[[0., 0., 1., 1.]]]), + multiples=[batch_size, 1, 1]) + detection_scores = tf.tile(tf.constant([[0.8]]), multiples=[batch_size, 1]) + detection_classes = tf.tile(tf.constant([[0]]), multiples=[batch_size, 1]) + detection_masks = tf.tile(tf.ones(shape=[1, 1, 20, 20], dtype=tf.float32), + multiples=[batch_size, 1, 1, 1]) + num_detections = tf.ones([batch_size]) + groundtruth_boxes = tf.constant([[0., 0., 1., 1.]]) + groundtruth_classes = tf.constant([1]) + groundtruth_instance_masks = tf.ones(shape=[1, 20, 20], dtype=tf.uint8) + original_image_spatial_shapes = tf.constant([[20, 20]], dtype=tf.int32) + + groundtruth_keypoints = tf.constant([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]]) + if resized_groundtruth_masks: + groundtruth_instance_masks = tf.ones(shape=[1, 10, 10], dtype=tf.uint8) + + if batch_size > 1: + groundtruth_boxes = tf.tile(tf.expand_dims(groundtruth_boxes, 0), + multiples=[batch_size, 1, 1]) + groundtruth_classes = tf.tile(tf.expand_dims(groundtruth_classes, 0), + multiples=[batch_size, 1]) + groundtruth_instance_masks = tf.tile( + tf.expand_dims(groundtruth_instance_masks, 0), + multiples=[batch_size, 1, 1, 1]) + groundtruth_keypoints = tf.tile( + tf.expand_dims(groundtruth_keypoints, 0), + multiples=[batch_size, 1, 1]) + original_image_spatial_shapes = tf.tile(original_image_spatial_shapes, + multiples=[batch_size, 1]) + + detections = { + detection_fields.detection_boxes: detection_boxes, + detection_fields.detection_scores: detection_scores, + detection_fields.detection_classes: detection_classes, + detection_fields.detection_masks: detection_masks, + detection_fields.num_detections: num_detections + } + groundtruth = { + input_data_fields.groundtruth_boxes: groundtruth_boxes, + input_data_fields.groundtruth_classes: groundtruth_classes, + input_data_fields.groundtruth_keypoints: groundtruth_keypoints, + input_data_fields.groundtruth_instance_masks: + groundtruth_instance_masks, + input_data_fields.original_image_spatial_shape: + original_image_spatial_shapes + } + if batch_size > 1: + return eval_util.result_dict_for_batched_example( + image, key, detections, groundtruth, + scale_to_absolute=scale_to_absolute, + max_gt_boxes=max_gt_boxes) + else: + return eval_util.result_dict_for_single_example( + image, key, detections, groundtruth, + scale_to_absolute=scale_to_absolute) + + @parameterized.parameters( + {'batch_size': 1, 'max_gt_boxes': None, 'scale_to_absolute': True}, + {'batch_size': 8, 'max_gt_boxes': [1], 'scale_to_absolute': True}, + {'batch_size': 1, 'max_gt_boxes': None, 'scale_to_absolute': False}, + {'batch_size': 8, 'max_gt_boxes': [1], 'scale_to_absolute': False} + ) + @unittest.skipIf(tf_version.is_tf2(), 'Only compatible with TF1.X') + def test_get_eval_metric_ops_for_coco_detections(self, batch_size=1, + max_gt_boxes=None, + scale_to_absolute=False): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend(['coco_detection_metrics']) + categories = self._get_categories_list() + eval_dict = self._make_evaluation_dict(batch_size=batch_size, + max_gt_boxes=max_gt_boxes, + scale_to_absolute=scale_to_absolute) + metric_ops = eval_util.get_eval_metric_ops_for_evaluators( + eval_config, categories, eval_dict) + _, update_op = metric_ops['DetectionBoxes_Precision/mAP'] + + with self.test_session() as sess: + metrics = {} + for key, (value_op, _) in six.iteritems(metric_ops): + metrics[key] = value_op + sess.run(update_op) + metrics = sess.run(metrics) + self.assertAlmostEqual(1.0, metrics['DetectionBoxes_Precision/mAP']) + self.assertNotIn('DetectionMasks_Precision/mAP', metrics) + + @parameterized.parameters( + {'batch_size': 1, 'max_gt_boxes': None, 'scale_to_absolute': True}, + {'batch_size': 8, 'max_gt_boxes': [1], 'scale_to_absolute': True}, + {'batch_size': 1, 'max_gt_boxes': None, 'scale_to_absolute': False}, + {'batch_size': 8, 'max_gt_boxes': [1], 'scale_to_absolute': False} + ) + @unittest.skipIf(tf_version.is_tf2(), 'Only compatible with TF1.X') + def test_get_eval_metric_ops_for_coco_detections_and_masks( + self, batch_size=1, max_gt_boxes=None, scale_to_absolute=False): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend( + ['coco_detection_metrics', 'coco_mask_metrics']) + categories = self._get_categories_list() + eval_dict = self._make_evaluation_dict(batch_size=batch_size, + max_gt_boxes=max_gt_boxes, + scale_to_absolute=scale_to_absolute) + metric_ops = eval_util.get_eval_metric_ops_for_evaluators( + eval_config, categories, eval_dict) + _, update_op_boxes = metric_ops['DetectionBoxes_Precision/mAP'] + _, update_op_masks = metric_ops['DetectionMasks_Precision/mAP'] + + with self.test_session() as sess: + metrics = {} + for key, (value_op, _) in six.iteritems(metric_ops): + metrics[key] = value_op + sess.run(update_op_boxes) + sess.run(update_op_masks) + metrics = sess.run(metrics) + self.assertAlmostEqual(1.0, metrics['DetectionBoxes_Precision/mAP']) + self.assertAlmostEqual(1.0, metrics['DetectionMasks_Precision/mAP']) + + @parameterized.parameters( + {'batch_size': 1, 'max_gt_boxes': None, 'scale_to_absolute': True}, + {'batch_size': 8, 'max_gt_boxes': [1], 'scale_to_absolute': True}, + {'batch_size': 1, 'max_gt_boxes': None, 'scale_to_absolute': False}, + {'batch_size': 8, 'max_gt_boxes': [1], 'scale_to_absolute': False} + ) + @unittest.skipIf(tf_version.is_tf2(), 'Only compatible with TF1.X') + def test_get_eval_metric_ops_for_coco_detections_and_resized_masks( + self, batch_size=1, max_gt_boxes=None, scale_to_absolute=False): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend( + ['coco_detection_metrics', 'coco_mask_metrics']) + categories = self._get_categories_list() + eval_dict = self._make_evaluation_dict(batch_size=batch_size, + max_gt_boxes=max_gt_boxes, + scale_to_absolute=scale_to_absolute, + resized_groundtruth_masks=True) + metric_ops = eval_util.get_eval_metric_ops_for_evaluators( + eval_config, categories, eval_dict) + _, update_op_boxes = metric_ops['DetectionBoxes_Precision/mAP'] + _, update_op_masks = metric_ops['DetectionMasks_Precision/mAP'] + + with self.test_session() as sess: + metrics = {} + for key, (value_op, _) in six.iteritems(metric_ops): + metrics[key] = value_op + sess.run(update_op_boxes) + sess.run(update_op_masks) + metrics = sess.run(metrics) + self.assertAlmostEqual(1.0, metrics['DetectionBoxes_Precision/mAP']) + self.assertAlmostEqual(1.0, metrics['DetectionMasks_Precision/mAP']) + + @unittest.skipIf(tf_version.is_tf2(), 'Only compatible with TF1.X') + def test_get_eval_metric_ops_raises_error_with_unsupported_metric(self): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend(['unsupported_metric']) + categories = self._get_categories_list() + eval_dict = self._make_evaluation_dict() + with self.assertRaises(ValueError): + eval_util.get_eval_metric_ops_for_evaluators( + eval_config, categories, eval_dict) + + def test_get_eval_metric_ops_for_evaluators(self): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend([ + 'coco_detection_metrics', 'coco_mask_metrics', + 'precision_at_recall_detection_metrics' + ]) + eval_config.include_metrics_per_category = True + eval_config.recall_lower_bound = 0.2 + eval_config.recall_upper_bound = 0.6 + + evaluator_options = eval_util.evaluator_options_from_eval_config( + eval_config) + self.assertTrue(evaluator_options['coco_detection_metrics'] + ['include_metrics_per_category']) + self.assertFalse(evaluator_options['coco_detection_metrics'] + ['skip_predictions_for_unlabeled_class']) + self.assertTrue( + evaluator_options['coco_mask_metrics']['include_metrics_per_category']) + self.assertAlmostEqual( + evaluator_options['precision_at_recall_detection_metrics'] + ['recall_lower_bound'], eval_config.recall_lower_bound) + self.assertAlmostEqual( + evaluator_options['precision_at_recall_detection_metrics'] + ['recall_upper_bound'], eval_config.recall_upper_bound) + + def test_get_evaluator_with_evaluator_options(self): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend( + ['coco_detection_metrics', 'precision_at_recall_detection_metrics']) + eval_config.include_metrics_per_category = True + eval_config.skip_predictions_for_unlabeled_class = True + eval_config.recall_lower_bound = 0.2 + eval_config.recall_upper_bound = 0.6 + categories = self._get_categories_list() + + evaluator_options = eval_util.evaluator_options_from_eval_config( + eval_config) + evaluator = eval_util.get_evaluators(eval_config, categories, + evaluator_options) + + self.assertTrue(evaluator[0]._include_metrics_per_category) + self.assertTrue(evaluator[0]._skip_predictions_for_unlabeled_class) + self.assertAlmostEqual(evaluator[1]._recall_lower_bound, + eval_config.recall_lower_bound) + self.assertAlmostEqual(evaluator[1]._recall_upper_bound, + eval_config.recall_upper_bound) + + def test_get_evaluator_with_no_evaluator_options(self): + eval_config = eval_pb2.EvalConfig() + eval_config.metrics_set.extend( + ['coco_detection_metrics', 'precision_at_recall_detection_metrics']) + eval_config.include_metrics_per_category = True + eval_config.recall_lower_bound = 0.2 + eval_config.recall_upper_bound = 0.6 + categories = self._get_categories_list() + + evaluator = eval_util.get_evaluators( + eval_config, categories, evaluator_options=None) + + # Even though we are setting eval_config.include_metrics_per_category = True + # and bounds on recall, these options are never passed into the + # DetectionEvaluator constructor (via `evaluator_options`). + self.assertFalse(evaluator[0]._include_metrics_per_category) + self.assertAlmostEqual(evaluator[1]._recall_lower_bound, 0.0) + self.assertAlmostEqual(evaluator[1]._recall_upper_bound, 1.0) + + def test_get_evaluator_with_keypoint_metrics(self): + eval_config = eval_pb2.EvalConfig() + person_keypoints_metric = eval_config.parameterized_metric.add() + person_keypoints_metric.coco_keypoint_metrics.class_label = 'person' + person_keypoints_metric.coco_keypoint_metrics.keypoint_label_to_sigmas[ + 'left_eye'] = 0.1 + person_keypoints_metric.coco_keypoint_metrics.keypoint_label_to_sigmas[ + 'right_eye'] = 0.2 + dog_keypoints_metric = eval_config.parameterized_metric.add() + dog_keypoints_metric.coco_keypoint_metrics.class_label = 'dog' + dog_keypoints_metric.coco_keypoint_metrics.keypoint_label_to_sigmas[ + 'tail_start'] = 0.3 + dog_keypoints_metric.coco_keypoint_metrics.keypoint_label_to_sigmas[ + 'mouth'] = 0.4 + categories = self._get_categories_list_with_keypoints() + + evaluator = eval_util.get_evaluators( + eval_config, categories, evaluator_options=None) + + # Verify keypoint evaluator class variables. + self.assertLen(evaluator, 3) + self.assertFalse(evaluator[0]._include_metrics_per_category) + self.assertEqual(evaluator[1]._category_name, 'person') + self.assertEqual(evaluator[2]._category_name, 'dog') + self.assertAllEqual(evaluator[1]._keypoint_ids, [0, 3]) + self.assertAllEqual(evaluator[2]._keypoint_ids, [1, 2]) + self.assertAllClose([0.1, 0.2], evaluator[1]._oks_sigmas) + self.assertAllClose([0.3, 0.4], evaluator[2]._oks_sigmas) + + def test_get_evaluator_with_unmatched_label(self): + eval_config = eval_pb2.EvalConfig() + person_keypoints_metric = eval_config.parameterized_metric.add() + person_keypoints_metric.coco_keypoint_metrics.class_label = 'unmatched' + person_keypoints_metric.coco_keypoint_metrics.keypoint_label_to_sigmas[ + 'kpt'] = 0.1 + categories = self._get_categories_list_with_keypoints() + + evaluator = eval_util.get_evaluators( + eval_config, categories, evaluator_options=None) + self.assertLen(evaluator, 1) + self.assertNotIsInstance( + evaluator[0], coco_evaluation.CocoKeypointEvaluator) + + def test_padded_image_result_dict(self): + + input_data_fields = fields.InputDataFields + detection_fields = fields.DetectionResultFields + key = tf.constant([str(i) for i in range(2)]) + + detection_boxes = np.array([[[0., 0., 1., 1.]], [[0.0, 0.0, 0.5, 0.5]]], + dtype=np.float32) + detection_keypoints = np.array([[0.0, 0.0], [0.5, 0.5], [1.0, 1.0]], + dtype=np.float32) + def graph_fn(): + detections = { + detection_fields.detection_boxes: + tf.constant(detection_boxes), + detection_fields.detection_scores: + tf.constant([[1.], [1.]]), + detection_fields.detection_classes: + tf.constant([[1], [2]]), + detection_fields.num_detections: + tf.constant([1, 1]), + detection_fields.detection_keypoints: + tf.tile( + tf.reshape( + tf.constant(detection_keypoints), shape=[1, 1, 3, 2]), + multiples=[2, 1, 1, 1]) + } + + gt_boxes = detection_boxes + groundtruth = { + input_data_fields.groundtruth_boxes: + tf.constant(gt_boxes), + input_data_fields.groundtruth_classes: + tf.constant([[1.], [1.]]), + input_data_fields.groundtruth_keypoints: + tf.tile( + tf.reshape( + tf.constant(detection_keypoints), shape=[1, 1, 3, 2]), + multiples=[2, 1, 1, 1]) + } + + image = tf.zeros((2, 100, 100, 3), dtype=tf.float32) + + true_image_shapes = tf.constant([[100, 100, 3], [50, 100, 3]]) + original_image_spatial_shapes = tf.constant([[200, 200], [150, 300]]) + + result = eval_util.result_dict_for_batched_example( + image, key, detections, groundtruth, + scale_to_absolute=True, + true_image_shapes=true_image_shapes, + original_image_spatial_shapes=original_image_spatial_shapes, + max_gt_boxes=tf.constant(1)) + return (result[input_data_fields.groundtruth_boxes], + result[input_data_fields.groundtruth_keypoints], + result[detection_fields.detection_boxes], + result[detection_fields.detection_keypoints]) + (gt_boxes, gt_keypoints, detection_boxes, + detection_keypoints) = self.execute_cpu(graph_fn, []) + self.assertAllEqual( + [[[0., 0., 200., 200.]], [[0.0, 0.0, 150., 150.]]], + gt_boxes) + self.assertAllClose([[[[0., 0.], [100., 100.], [200., 200.]]], + [[[0., 0.], [150., 150.], [300., 300.]]]], + gt_keypoints) + + # Predictions from the model are not scaled. + self.assertAllEqual( + [[[0., 0., 200., 200.]], [[0.0, 0.0, 75., 150.]]], + detection_boxes) + self.assertAllClose([[[[0., 0.], [100., 100.], [200., 200.]]], + [[[0., 0.], [75., 150.], [150., 300.]]]], + detection_keypoints) + + def test_evaluator_options_from_eval_config_no_super_categories(self): + eval_config_text_proto = """ + metrics_set: "coco_detection_metrics" + metrics_set: "coco_mask_metrics" + include_metrics_per_category: true + use_moving_averages: false + batch_size: 1; + """ + eval_config = eval_pb2.EvalConfig() + text_format.Merge(eval_config_text_proto, eval_config) + evaluator_options = eval_util.evaluator_options_from_eval_config( + eval_config) + self.assertNotIn('super_categories', evaluator_options['coco_mask_metrics']) + + def test_evaluator_options_from_eval_config_with_super_categories(self): + eval_config_text_proto = """ + metrics_set: "coco_detection_metrics" + metrics_set: "coco_mask_metrics" + include_metrics_per_category: true + use_moving_averages: false + batch_size: 1; + super_categories { + key: "supercat1" + value: "a,b,c" + } + super_categories { + key: "supercat2" + value: "d,e,f" + } + """ + eval_config = eval_pb2.EvalConfig() + text_format.Merge(eval_config_text_proto, eval_config) + evaluator_options = eval_util.evaluator_options_from_eval_config( + eval_config) + self.assertIn('super_categories', evaluator_options['coco_mask_metrics']) + super_categories = evaluator_options[ + 'coco_mask_metrics']['super_categories'] + self.assertIn('supercat1', super_categories) + self.assertIn('supercat2', super_categories) + self.assertAllEqual(super_categories['supercat1'], ['a', 'b', 'c']) + self.assertAllEqual(super_categories['supercat2'], ['d', 'e', 'f']) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/export_inference_graph.py b/workspace/virtuallab/object_detection/export_inference_graph.py new file mode 100644 index 0000000..5a0ee0d --- /dev/null +++ b/workspace/virtuallab/object_detection/export_inference_graph.py @@ -0,0 +1,206 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +r"""Tool to export an object detection model for inference. + +Prepares an object detection tensorflow graph for inference using model +configuration and a trained checkpoint. Outputs inference +graph, associated checkpoint files, a frozen inference graph and a +SavedModel (https://tensorflow.github.io/serving/serving_basic.html). + +The inference graph contains one of three input nodes depending on the user +specified option. + * `image_tensor`: Accepts a uint8 4-D tensor of shape [None, None, None, 3] + * `encoded_image_string_tensor`: Accepts a 1-D string tensor of shape [None] + containing encoded PNG or JPEG images. Image resolutions are expected to be + the same if more than 1 image is provided. + * `tf_example`: Accepts a 1-D string tensor of shape [None] containing + serialized TFExample protos. Image resolutions are expected to be the same + if more than 1 image is provided. + +and the following output nodes returned by the model.postprocess(..): + * `num_detections`: Outputs float32 tensors of the form [batch] + that specifies the number of valid boxes per image in the batch. + * `detection_boxes`: Outputs float32 tensors of the form + [batch, num_boxes, 4] containing detected boxes. + * `detection_scores`: Outputs float32 tensors of the form + [batch, num_boxes] containing class scores for the detections. + * `detection_classes`: Outputs float32 tensors of the form + [batch, num_boxes] containing classes for the detections. + * `raw_detection_boxes`: Outputs float32 tensors of the form + [batch, raw_num_boxes, 4] containing detection boxes without + post-processing. + * `raw_detection_scores`: Outputs float32 tensors of the form + [batch, raw_num_boxes, num_classes_with_background] containing class score + logits for raw detection boxes. + * `detection_masks`: (Optional) Outputs float32 tensors of the form + [batch, num_boxes, mask_height, mask_width] containing predicted instance + masks for each box if its present in the dictionary of postprocessed + tensors returned by the model. + * detection_multiclass_scores: (Optional) Outputs float32 tensor of shape + [batch, num_boxes, num_classes_with_background] for containing class + score distribution for detected boxes including background if any. + * detection_features: (Optional) float32 tensor of shape + [batch, num_boxes, roi_height, roi_width, depth] + containing classifier features + +Notes: + * This tool uses `use_moving_averages` from eval_config to decide which + weights to freeze. + +Example Usage: +-------------- +python export_inference_graph.py \ + --input_type image_tensor \ + --pipeline_config_path path/to/ssd_inception_v2.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory + +The expected output would be in the directory +path/to/exported_model_directory (which is created if it does not exist) +with contents: + - inference_graph.pbtxt + - model.ckpt.data-00000-of-00001 + - model.ckpt.info + - model.ckpt.meta + - frozen_inference_graph.pb + + saved_model (a directory) + +Config overrides (see the `config_override` flag) are text protobufs +(also of type pipeline_pb2.TrainEvalPipelineConfig) which are used to override +certain fields in the provided pipeline_config_path. These are useful for +making small changes to the inference graph that differ from the training or +eval config. + +Example Usage (in which we change the second stage post-processing score +threshold to be 0.5): + +python export_inference_graph.py \ + --input_type image_tensor \ + --pipeline_config_path path/to/ssd_inception_v2.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory \ + --config_override " \ + model{ \ + faster_rcnn { \ + second_stage_post_processing { \ + batch_non_max_suppression { \ + score_threshold: 0.5 \ + } \ + } \ + } \ + }" +""" +import tensorflow.compat.v1 as tf +from google.protobuf import text_format +from object_detection import exporter +from object_detection.protos import pipeline_pb2 + +flags = tf.app.flags + +flags.DEFINE_string('input_type', 'image_tensor', 'Type of input node. Can be ' + 'one of [`image_tensor`, `encoded_image_string_tensor`, ' + '`tf_example`]') +flags.DEFINE_string('input_shape', None, + 'If input_type is `image_tensor`, this can explicitly set ' + 'the shape of this input tensor to a fixed size. The ' + 'dimensions are to be provided as a comma-separated list ' + 'of integers. A value of -1 can be used for unknown ' + 'dimensions. If not specified, for an `image_tensor, the ' + 'default shape will be partially specified as ' + '`[None, None, None, 3]`.') +flags.DEFINE_string('pipeline_config_path', None, + 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' + 'file.') +flags.DEFINE_string('trained_checkpoint_prefix', None, + 'Path to trained checkpoint, typically of the form ' + 'path/to/model.ckpt') +flags.DEFINE_string('output_directory', None, 'Path to write outputs.') +flags.DEFINE_string('config_override', '', + 'pipeline_pb2.TrainEvalPipelineConfig ' + 'text proto to override pipeline_config_path.') +flags.DEFINE_boolean('write_inference_graph', False, + 'If true, writes inference graph to disk.') +flags.DEFINE_string('additional_output_tensor_names', None, + 'Additional Tensors to output, to be specified as a comma ' + 'separated list of tensor names.') +flags.DEFINE_boolean('use_side_inputs', False, + 'If True, uses side inputs as well as image inputs.') +flags.DEFINE_string('side_input_shapes', None, + 'If use_side_inputs is True, this explicitly sets ' + 'the shape of the side input tensors to a fixed size. The ' + 'dimensions are to be provided as a comma-separated list ' + 'of integers. A value of -1 can be used for unknown ' + 'dimensions. A `/` denotes a break, starting the shape of ' + 'the next side input tensor. This flag is required if ' + 'using side inputs.') +flags.DEFINE_string('side_input_types', None, + 'If use_side_inputs is True, this explicitly sets ' + 'the type of the side input tensors. The ' + 'dimensions are to be provided as a comma-separated list ' + 'of types, each of `string`, `integer`, or `float`. ' + 'This flag is required if using side inputs.') +flags.DEFINE_string('side_input_names', None, + 'If use_side_inputs is True, this explicitly sets ' + 'the names of the side input tensors required by the model ' + 'assuming the names will be a comma-separated list of ' + 'strings. This flag is required if using side inputs.') +tf.app.flags.mark_flag_as_required('pipeline_config_path') +tf.app.flags.mark_flag_as_required('trained_checkpoint_prefix') +tf.app.flags.mark_flag_as_required('output_directory') +FLAGS = flags.FLAGS + + +def main(_): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + with tf.gfile.GFile(FLAGS.pipeline_config_path, 'r') as f: + text_format.Merge(f.read(), pipeline_config) + text_format.Merge(FLAGS.config_override, pipeline_config) + if FLAGS.input_shape: + input_shape = [ + int(dim) if dim != '-1' else None + for dim in FLAGS.input_shape.split(',') + ] + else: + input_shape = None + if FLAGS.use_side_inputs: + side_input_shapes, side_input_names, side_input_types = ( + exporter.parse_side_inputs( + FLAGS.side_input_shapes, + FLAGS.side_input_names, + FLAGS.side_input_types)) + else: + side_input_shapes = None + side_input_names = None + side_input_types = None + if FLAGS.additional_output_tensor_names: + additional_output_tensor_names = list( + FLAGS.additional_output_tensor_names.split(',')) + else: + additional_output_tensor_names = None + exporter.export_inference_graph( + FLAGS.input_type, pipeline_config, FLAGS.trained_checkpoint_prefix, + FLAGS.output_directory, input_shape=input_shape, + write_inference_graph=FLAGS.write_inference_graph, + additional_output_tensor_names=additional_output_tensor_names, + use_side_inputs=FLAGS.use_side_inputs, + side_input_shapes=side_input_shapes, + side_input_names=side_input_names, + side_input_types=side_input_types) + + +if __name__ == '__main__': + tf.app.run() diff --git a/workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2.py b/workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2.py new file mode 100644 index 0000000..6078953 --- /dev/null +++ b/workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2.py @@ -0,0 +1,254 @@ +# Lint as: python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Library to export TFLite-compatible SavedModel from TF2 detection models.""" +import os +import numpy as np +import tensorflow.compat.v1 as tf1 +import tensorflow.compat.v2 as tf + +from object_detection.builders import model_builder +from object_detection.builders import post_processing_builder +from object_detection.core import box_list + +_DEFAULT_NUM_CHANNELS = 3 +_DEFAULT_NUM_COORD_BOX = 4 +_MAX_CLASSES_PER_DETECTION = 1 +_DETECTION_POSTPROCESS_FUNC = 'TFLite_Detection_PostProcess' + + +def get_const_center_size_encoded_anchors(anchors): + """Exports center-size encoded anchors as a constant tensor. + + Args: + anchors: a float32 tensor of shape [num_anchors, 4] containing the anchor + boxes + + Returns: + encoded_anchors: a float32 constant tensor of shape [num_anchors, 4] + containing the anchor boxes. + """ + anchor_boxlist = box_list.BoxList(anchors) + y, x, h, w = anchor_boxlist.get_center_coordinates_and_sizes() + num_anchors = y.get_shape().as_list() + + with tf1.Session() as sess: + y_out, x_out, h_out, w_out = sess.run([y, x, h, w]) + encoded_anchors = tf1.constant( + np.transpose(np.stack((y_out, x_out, h_out, w_out))), + dtype=tf1.float32, + shape=[num_anchors[0], _DEFAULT_NUM_COORD_BOX], + name='anchors') + return num_anchors[0], encoded_anchors + + +class SSDModule(tf.Module): + """Inference Module for TFLite-friendly SSD models.""" + + def __init__(self, pipeline_config, detection_model, max_detections, + use_regular_nms): + """Initialization. + + Args: + pipeline_config: The original pipeline_pb2.TrainEvalPipelineConfig + detection_model: The detection model to use for inference. + max_detections: Max detections desired from the TFLite model. + use_regular_nms: If True, TFLite model uses the (slower) multi-class NMS. + """ + self._process_config(pipeline_config) + self._pipeline_config = pipeline_config + self._model = detection_model + self._max_detections = max_detections + self._use_regular_nms = use_regular_nms + + def _process_config(self, pipeline_config): + self._num_classes = pipeline_config.model.ssd.num_classes + self._nms_score_threshold = pipeline_config.model.ssd.post_processing.batch_non_max_suppression.score_threshold + self._nms_iou_threshold = pipeline_config.model.ssd.post_processing.batch_non_max_suppression.iou_threshold + self._scale_values = {} + self._scale_values[ + 'y_scale'] = pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale + self._scale_values[ + 'x_scale'] = pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale + self._scale_values[ + 'h_scale'] = pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale + self._scale_values[ + 'w_scale'] = pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale + + image_resizer_config = pipeline_config.model.ssd.image_resizer + image_resizer = image_resizer_config.WhichOneof('image_resizer_oneof') + self._num_channels = _DEFAULT_NUM_CHANNELS + + if image_resizer == 'fixed_shape_resizer': + self._height = image_resizer_config.fixed_shape_resizer.height + self._width = image_resizer_config.fixed_shape_resizer.width + if image_resizer_config.fixed_shape_resizer.convert_to_grayscale: + self._num_channels = 1 + else: + raise ValueError( + 'Only fixed_shape_resizer' + 'is supported with tflite. Found {}'.format( + image_resizer_config.WhichOneof('image_resizer_oneof'))) + + def input_shape(self): + """Returns shape of TFLite model input.""" + return [1, self._height, self._width, self._num_channels] + + def postprocess_implements_signature(self): + """Returns tf.implements signature for MLIR legalization of TFLite NMS.""" + implements_signature = [ + 'name: "%s"' % _DETECTION_POSTPROCESS_FUNC, + 'attr { key: "max_detections" value { i: %d } }' % self._max_detections, + 'attr { key: "max_classes_per_detection" value { i: %d } }' % + _MAX_CLASSES_PER_DETECTION, + 'attr { key: "use_regular_nms" value { b: %s } }' % + str(self._use_regular_nms).lower(), + 'attr { key: "nms_score_threshold" value { f: %f } }' % + self._nms_score_threshold, + 'attr { key: "nms_iou_threshold" value { f: %f } }' % + self._nms_iou_threshold, + 'attr { key: "y_scale" value { f: %f } }' % + self._scale_values['y_scale'], + 'attr { key: "x_scale" value { f: %f } }' % + self._scale_values['x_scale'], + 'attr { key: "h_scale" value { f: %f } }' % + self._scale_values['h_scale'], + 'attr { key: "w_scale" value { f: %f } }' % + self._scale_values['w_scale'], + 'attr { key: "num_classes" value { i: %d } }' % self._num_classes + ] + implements_signature = ' '.join(implements_signature) + return implements_signature + + def _get_postprocess_fn(self, num_anchors, num_classes): + # There is no TF equivalent for TFLite's custom post-processing op. + # So we add an 'empty' composite function here, that is legalized to the + # custom op with MLIR. + @tf.function( + experimental_implements=self.postprocess_implements_signature()) + # pylint: disable=g-unused-argument,unused-argument + def dummy_post_processing(box_encodings, class_predictions, anchors): + boxes = tf.constant(0.0, dtype=tf.float32, name='boxes') + scores = tf.constant(0.0, dtype=tf.float32, name='scores') + classes = tf.constant(0.0, dtype=tf.float32, name='classes') + num_detections = tf.constant(0.0, dtype=tf.float32, name='num_detections') + return boxes, scores, classes, num_detections + + return dummy_post_processing + + @tf.function + def inference_fn(self, image): + """Encapsulates SSD inference for TFLite conversion. + + NOTE: The Args & Returns sections below indicate the TFLite model signature, + and not what the TF graph does (since the latter does not include the custom + NMS op used by TFLite) + + Args: + image: a float32 tensor of shape [num_anchors, 4] containing the anchor + boxes + + Returns: + num_detections: a float32 scalar denoting number of total detections. + classes: a float32 tensor denoting class ID for each detection. + scores: a float32 tensor denoting score for each detection. + boxes: a float32 tensor denoting coordinates of each detected box. + """ + predicted_tensors = self._model.predict(image, true_image_shapes=None) + # The score conversion occurs before the post-processing custom op + _, score_conversion_fn = post_processing_builder.build( + self._pipeline_config.model.ssd.post_processing) + class_predictions = score_conversion_fn( + predicted_tensors['class_predictions_with_background']) + + with tf.name_scope('raw_outputs'): + # 'raw_outputs/box_encodings': a float32 tensor of shape + # [1, num_anchors, 4] containing the encoded box predictions. Note that + # these are raw predictions and no Non-Max suppression is applied on + # them and no decode center size boxes is applied to them. + box_encodings = tf.identity( + predicted_tensors['box_encodings'], name='box_encodings') + # 'raw_outputs/class_predictions': a float32 tensor of shape + # [1, num_anchors, num_classes] containing the class scores for each + # anchor after applying score conversion. + class_predictions = tf.identity( + class_predictions, name='class_predictions') + # 'anchors': a float32 tensor of shape + # [4, num_anchors] containing the anchors as a constant node. + num_anchors, anchors = get_const_center_size_encoded_anchors( + predicted_tensors['anchors']) + anchors = tf.identity(anchors, name='anchors') + + # tf.function@ seems to reverse order of inputs, so reverse them here. + return self._get_postprocess_fn(num_anchors, + self._num_classes)(box_encodings, + class_predictions, + anchors)[::-1] + + +def export_tflite_model(pipeline_config, trained_checkpoint_dir, + output_directory, max_detections, use_regular_nms): + """Exports inference SavedModel for TFLite conversion. + + NOTE: Only supports SSD meta-architectures for now, and the output model will + have static-shaped, single-batch input. + + This function creates `output_directory` if it does not already exist, + which will hold the intermediate SavedModel that can be used with the TFLite + converter. + + Args: + pipeline_config: pipeline_pb2.TrainAndEvalPipelineConfig proto. + trained_checkpoint_dir: Path to the trained checkpoint file. + output_directory: Path to write outputs. + max_detections: Max detections desired from the TFLite model. + use_regular_nms: If True, TFLite model uses the (slower) multi-class NMS. + + Raises: + ValueError: if pipeline is invalid. + """ + output_saved_model_directory = os.path.join(output_directory, 'saved_model') + + # Build the underlying model using pipeline config. + # TODO(b/162842801): Add support for other architectures. + if pipeline_config.model.WhichOneof('model') != 'ssd': + raise ValueError('Only ssd models are supported in tflite. ' + 'Found {} in config'.format( + pipeline_config.model.WhichOneof('model'))) + detection_model = model_builder.build( + pipeline_config.model, is_training=False) + + ckpt = tf.train.Checkpoint(model=detection_model) + manager = tf.train.CheckpointManager( + ckpt, trained_checkpoint_dir, max_to_keep=1) + status = ckpt.restore(manager.latest_checkpoint).expect_partial() + + # The module helps build a TF SavedModel appropriate for TFLite conversion. + detection_module = SSDModule(pipeline_config, detection_model, max_detections, + use_regular_nms) + + # Getting the concrete function traces the graph and forces variables to + # be constructed; only after this can we save the saved model. + status.assert_existing_objects_matched() + concrete_function = detection_module.inference_fn.get_concrete_function( + tf.TensorSpec( + shape=detection_module.input_shape(), dtype=tf.float32, name='input')) + status.assert_existing_objects_matched() + + # Export SavedModel. + tf.saved_model.save( + detection_module, + output_saved_model_directory, + signatures=concrete_function) diff --git a/workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2_test.py b/workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2_test.py new file mode 100644 index 0000000..b4b1ea4 --- /dev/null +++ b/workspace/virtuallab/object_detection/export_tflite_graph_lib_tf2_test.py @@ -0,0 +1,245 @@ +# Lint as: python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Test for export_tflite_graph_lib_tf2.py.""" + +from __future__ import division +import os +import unittest +import six + +import tensorflow.compat.v2 as tf + +from object_detection import export_tflite_graph_lib_tf2 +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.protos import pipeline_pb2 +from object_detection.utils import tf_version + +if six.PY2: + import mock # pylint: disable=g-importing-member,g-import-not-at-top +else: + from unittest import mock # pylint: disable=g-importing-member,g-import-not-at-top + + +class FakeModel(model.DetectionModel): + + def __init__(self): + super(FakeModel, self).__init__(num_classes=2) + self._conv = tf.keras.layers.Conv2D( + filters=1, + kernel_size=1, + strides=(1, 1), + padding='valid', + kernel_initializer=tf.keras.initializers.Constant(value=1.0)) + + def preprocess(self, inputs): + true_image_shapes = [] # Doesn't matter for the fake model. + return tf.identity(inputs), true_image_shapes + + def predict(self, preprocessed_inputs, true_image_shapes): + prediction_tensors = {'image': self._conv(preprocessed_inputs)} + with tf.control_dependencies([prediction_tensors['image']]): + prediction_tensors['box_encodings'] = tf.constant( + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]], tf.float32) + prediction_tensors['class_predictions_with_background'] = tf.constant( + [[[0.7, 0.6], [0.9, 0.0]]], tf.float32) + with tf.control_dependencies([ + tf.convert_to_tensor( + prediction_tensors['image'].get_shape().as_list()[1:3]) + ]): + prediction_tensors['anchors'] = tf.constant( + [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 1.0]], tf.float32) + return prediction_tensors + + def postprocess(self, prediction_dict, true_image_shapes): + predict_tensor_sum = tf.reduce_sum(prediction_dict['image']) + with tf.control_dependencies(list(prediction_dict.values())): + postprocessed_tensors = { + 'detection_boxes': + tf.constant([[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], [0.0, 0.0, 0.0, 0.0]]], + tf.float32), + 'detection_scores': + predict_tensor_sum + + tf.constant([[0.7, 0.6], [0.9, 0.0]], tf.float32), + 'detection_classes': + tf.constant([[0, 1], [1, 0]], tf.float32), + 'num_detections': + tf.constant([2, 1], tf.float32), + } + return postprocessed_tensors + + def restore_map(self, checkpoint_path, from_detection_checkpoint): + pass + + def restore_from_objects(self, fine_tune_checkpoint_type): + pass + + def loss(self, prediction_dict, true_image_shapes): + pass + + def regularization_losses(self): + pass + + def updates(self): + pass + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class ExportTfLiteGraphTest(tf.test.TestCase): + + def _save_checkpoint_from_mock_model(self, checkpoint_dir): + mock_model = FakeModel() + fake_image = tf.zeros(shape=[1, 10, 10, 3], dtype=tf.float32) + preprocessed_inputs, true_image_shapes = mock_model.preprocess(fake_image) + predictions = mock_model.predict(preprocessed_inputs, true_image_shapes) + mock_model.postprocess(predictions, true_image_shapes) + + ckpt = tf.train.Checkpoint(model=mock_model) + exported_checkpoint_manager = tf.train.CheckpointManager( + ckpt, checkpoint_dir, max_to_keep=1) + exported_checkpoint_manager.save(checkpoint_number=0) + + def _get_ssd_config(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + pipeline_config.model.ssd.post_processing.batch_non_max_suppression.iou_threshold = 0.5 + return pipeline_config + + # The tf.implements signature is important since it ensures MLIR legalization, + # so we test it here. + def test_postprocess_implements_signature(self): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + pipeline_config = self._get_ssd_config() + + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + + detection_model = model_builder.build( + pipeline_config.model, is_training=False) + + ckpt = tf.train.Checkpoint(model=detection_model) + manager = tf.train.CheckpointManager(ckpt, tmp_dir, max_to_keep=1) + ckpt.restore(manager.latest_checkpoint).expect_partial() + + # The module helps build a TF graph appropriate for TFLite conversion. + detection_module = export_tflite_graph_lib_tf2.SSDModule( + pipeline_config=pipeline_config, + detection_model=detection_model, + max_detections=20, + use_regular_nms=True) + + expected_signature = ('name: "TFLite_Detection_PostProcess" attr { key: ' + '"max_detections" value { i: 20 } } attr { key: ' + '"max_classes_per_detection" value { i: 1 } } attr ' + '{ key: "use_regular_nms" value { b: true } } attr ' + '{ key: "nms_score_threshold" value { f: 0.000000 }' + ' } attr { key: "nms_iou_threshold" value { f: ' + '0.500000 } } attr { key: "y_scale" value { f: ' + '10.000000 } } attr { key: "x_scale" value { f: ' + '10.000000 } } attr { key: "h_scale" value { f: ' + '5.000000 } } attr { key: "w_scale" value { f: ' + '5.000000 } } attr { key: "num_classes" value { i: ' + '2 } }') + + self.assertEqual(expected_signature, + detection_module.postprocess_implements_signature()) + + def test_unsupported_architecture(self): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.model.faster_rcnn.num_classes = 10 + + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + expected_message = 'Only ssd models are supported in tflite' + try: + export_tflite_graph_lib_tf2.export_tflite_model( + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory, + max_detections=10, + use_regular_nms=False) + except ValueError as e: + if expected_message not in str(e): + raise + else: + raise AssertionError('Exception not raised: %s' % expected_message) + + def test_export_yields_saved_model(self): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + export_tflite_graph_lib_tf2.export_tflite_model( + pipeline_config=self._get_ssd_config(), + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory, + max_detections=10, + use_regular_nms=False) + self.assertTrue( + os.path.exists( + os.path.join(output_directory, 'saved_model', 'saved_model.pb'))) + self.assertTrue( + os.path.exists( + os.path.join(output_directory, 'saved_model', 'variables', + 'variables.index'))) + self.assertTrue( + os.path.exists( + os.path.join(output_directory, 'saved_model', 'variables', + 'variables.data-00000-of-00001'))) + + def test_exported_model_inference(self): + tmp_dir = self.get_temp_dir() + output_directory = os.path.join(tmp_dir, 'output') + self._save_checkpoint_from_mock_model(tmp_dir) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + export_tflite_graph_lib_tf2.export_tflite_model( + pipeline_config=self._get_ssd_config(), + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory, + max_detections=10, + use_regular_nms=False) + + saved_model_path = os.path.join(output_directory, 'saved_model') + detect_fn = tf.saved_model.load(saved_model_path) + detect_fn_sig = detect_fn.signatures['serving_default'] + image = tf.zeros(shape=[1, 10, 10, 3], dtype=tf.float32) + detections = detect_fn_sig(image) + + # The exported graph doesn't have numerically correct outputs, but there + # should be 4. + self.assertEqual(4, len(detections)) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/export_tflite_graph_tf2.py b/workspace/virtuallab/object_detection/export_tflite_graph_tf2.py new file mode 100644 index 0000000..0efe633 --- /dev/null +++ b/workspace/virtuallab/object_detection/export_tflite_graph_tf2.py @@ -0,0 +1,126 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Exports TF2 detection SavedModel for conversion to TensorFlow Lite. + +Link to the TF2 Detection Zoo: +https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md +The output folder will contain an intermediate SavedModel that can be used with +the TfLite converter. + +NOTE: This only supports SSD meta-architectures for now. + +One input: + image: a float32 tensor of shape[1, height, width, 3] containing the + *normalized* input image. + NOTE: See the `preprocess` function defined in the feature extractor class + in the object_detection/models directory. + +Four Outputs: + detection_boxes: a float32 tensor of shape [1, num_boxes, 4] with box + locations + detection_classes: a float32 tensor of shape [1, num_boxes] + with class indices + detection_scores: a float32 tensor of shape [1, num_boxes] + with class scores + num_boxes: a float32 tensor of size 1 containing the number of detected boxes + +Example Usage: +-------------- +python object_detection/export_tflite_graph_tf2.py \ + --pipeline_config_path path/to/ssd_model/pipeline.config \ + --trained_checkpoint_dir path/to/ssd_model/checkpoint \ + --output_directory path/to/exported_model_directory + +The expected output SavedModel would be in the directory +path/to/exported_model_directory (which is created if it does not exist). + +Config overrides (see the `config_override` flag) are text protobufs +(also of type pipeline_pb2.TrainEvalPipelineConfig) which are used to override +certain fields in the provided pipeline_config_path. These are useful for +making small changes to the inference graph that differ from the training or +eval config. + +Example Usage (in which we change the NMS iou_threshold to be 0.5 and +NMS score_threshold to be 0.0): +python object_detection/export_tflite_model_tf2.py \ + --pipeline_config_path path/to/ssd_model/pipeline.config \ + --trained_checkpoint_dir path/to/ssd_model/checkpoint \ + --output_directory path/to/exported_model_directory + --config_override " \ + model{ \ + ssd{ \ + post_processing { \ + batch_non_max_suppression { \ + score_threshold: 0.0 \ + iou_threshold: 0.5 \ + } \ + } \ + } \ + } \ + " +""" +from absl import app +from absl import flags + +import tensorflow.compat.v2 as tf +from google.protobuf import text_format +from object_detection import export_tflite_graph_lib_tf2 +from object_detection.protos import pipeline_pb2 + +tf.enable_v2_behavior() + +FLAGS = flags.FLAGS + +flags.DEFINE_string( + 'pipeline_config_path', None, + 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' + 'file.') +flags.DEFINE_string('trained_checkpoint_dir', None, + 'Path to trained checkpoint directory') +flags.DEFINE_string('output_directory', None, 'Path to write outputs.') +flags.DEFINE_string( + 'config_override', '', 'pipeline_pb2.TrainEvalPipelineConfig ' + 'text proto to override pipeline_config_path.') +# SSD-specific flags +flags.DEFINE_integer('ssd_max_detections', 10, + 'Maximum number of detections (boxes) to return.') +flags.DEFINE_bool( + 'ssd_use_regular_nms', False, + 'Flag to set postprocessing op to use Regular NMS instead of Fast NMS ' + '(Default false).') + + +def main(argv): + del argv # Unused. + flags.mark_flag_as_required('pipeline_config_path') + flags.mark_flag_as_required('trained_checkpoint_dir') + flags.mark_flag_as_required('output_directory') + + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + + with tf.io.gfile.GFile(FLAGS.pipeline_config_path, 'r') as f: + text_format.Parse(f.read(), pipeline_config) + text_format.Parse(FLAGS.config_override, pipeline_config) + + export_tflite_graph_lib_tf2.export_tflite_model(pipeline_config, + FLAGS.trained_checkpoint_dir, + FLAGS.output_directory, + FLAGS.ssd_max_detections, + FLAGS.ssd_use_regular_nms) + + +if __name__ == '__main__': + app.run(main) diff --git a/workspace/virtuallab/object_detection/export_tflite_ssd_graph.py b/workspace/virtuallab/object_detection/export_tflite_ssd_graph.py new file mode 100644 index 0000000..2127ca0 --- /dev/null +++ b/workspace/virtuallab/object_detection/export_tflite_ssd_graph.py @@ -0,0 +1,144 @@ +# Lint as: python2, python3 +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +r"""Exports an SSD detection model to use with tf-lite. + +Outputs file: +* A tflite compatible frozen graph - $output_directory/tflite_graph.pb + +The exported graph has the following input and output nodes. + +Inputs: +'normalized_input_image_tensor': a float32 tensor of shape +[1, height, width, 3] containing the normalized input image. Note that the +height and width must be compatible with the height and width configured in +the fixed_shape_image resizer options in the pipeline config proto. + +In floating point Mobilenet model, 'normalized_image_tensor' has values +between [-1,1). This typically means mapping each pixel (linearly) +to a value between [-1, 1]. Input image +values between 0 and 255 are scaled by (1/128.0) and then a value of +-1 is added to them to ensure the range is [-1,1). +In quantized Mobilenet model, 'normalized_image_tensor' has values between [0, +255]. +In general, see the `preprocess` function defined in the feature extractor class +in the object_detection/models directory. + +Outputs: +If add_postprocessing_op is true: frozen graph adds a + TFLite_Detection_PostProcess custom op node has four outputs: + detection_boxes: a float32 tensor of shape [1, num_boxes, 4] with box + locations + detection_classes: a float32 tensor of shape [1, num_boxes] + with class indices + detection_scores: a float32 tensor of shape [1, num_boxes] + with class scores + num_boxes: a float32 tensor of size 1 containing the number of detected boxes +else: + the graph has two outputs: + 'raw_outputs/box_encodings': a float32 tensor of shape [1, num_anchors, 4] + containing the encoded box predictions. + 'raw_outputs/class_predictions': a float32 tensor of shape + [1, num_anchors, num_classes] containing the class scores for each anchor + after applying score conversion. + +Example Usage: +-------------- +python object_detection/export_tflite_ssd_graph.py \ + --pipeline_config_path path/to/ssd_mobilenet.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory + +The expected output would be in the directory +path/to/exported_model_directory (which is created if it does not exist) +with contents: + - tflite_graph.pbtxt + - tflite_graph.pb +Config overrides (see the `config_override` flag) are text protobufs +(also of type pipeline_pb2.TrainEvalPipelineConfig) which are used to override +certain fields in the provided pipeline_config_path. These are useful for +making small changes to the inference graph that differ from the training or +eval config. + +Example Usage (in which we change the NMS iou_threshold to be 0.5 and +NMS score_threshold to be 0.0): +python object_detection/export_tflite_ssd_graph.py \ + --pipeline_config_path path/to/ssd_mobilenet.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory + --config_override " \ + model{ \ + ssd{ \ + post_processing { \ + batch_non_max_suppression { \ + score_threshold: 0.0 \ + iou_threshold: 0.5 \ + } \ + } \ + } \ + } \ + " +""" + +import tensorflow.compat.v1 as tf +from google.protobuf import text_format +from object_detection import export_tflite_ssd_graph_lib +from object_detection.protos import pipeline_pb2 + +flags = tf.app.flags +flags.DEFINE_string('output_directory', None, 'Path to write outputs.') +flags.DEFINE_string( + 'pipeline_config_path', None, + 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' + 'file.') +flags.DEFINE_string('trained_checkpoint_prefix', None, 'Checkpoint prefix.') +flags.DEFINE_integer('max_detections', 10, + 'Maximum number of detections (boxes) to show.') +flags.DEFINE_integer('max_classes_per_detection', 1, + 'Maximum number of classes to output per detection box.') +flags.DEFINE_integer( + 'detections_per_class', 100, + 'Number of anchors used per class in Regular Non-Max-Suppression.') +flags.DEFINE_bool('add_postprocessing_op', True, + 'Add TFLite custom op for postprocessing to the graph.') +flags.DEFINE_bool( + 'use_regular_nms', False, + 'Flag to set postprocessing op to use Regular NMS instead of Fast NMS.') +flags.DEFINE_string( + 'config_override', '', 'pipeline_pb2.TrainEvalPipelineConfig ' + 'text proto to override pipeline_config_path.') + +FLAGS = flags.FLAGS + + +def main(argv): + del argv # Unused. + flags.mark_flag_as_required('output_directory') + flags.mark_flag_as_required('pipeline_config_path') + flags.mark_flag_as_required('trained_checkpoint_prefix') + + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + + with tf.gfile.GFile(FLAGS.pipeline_config_path, 'r') as f: + text_format.Merge(f.read(), pipeline_config) + text_format.Merge(FLAGS.config_override, pipeline_config) + export_tflite_ssd_graph_lib.export_tflite_graph( + pipeline_config, FLAGS.trained_checkpoint_prefix, FLAGS.output_directory, + FLAGS.add_postprocessing_op, FLAGS.max_detections, + FLAGS.max_classes_per_detection, use_regular_nms=FLAGS.use_regular_nms) + + +if __name__ == '__main__': + tf.app.run(main) diff --git a/workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib.py b/workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib.py new file mode 100644 index 0000000..f72e952 --- /dev/null +++ b/workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib.py @@ -0,0 +1,334 @@ +# Lint as: python2, python3 +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Exports an SSD detection model to use with tf-lite. + +See export_tflite_ssd_graph.py for usage. +""" +import os +import tempfile +import numpy as np +import tensorflow.compat.v1 as tf +from tensorflow.core.framework import attr_value_pb2 +from tensorflow.core.framework import types_pb2 +from tensorflow.core.protobuf import saver_pb2 +from object_detection import exporter +from object_detection.builders import graph_rewriter_builder +from object_detection.builders import model_builder +from object_detection.builders import post_processing_builder +from object_detection.core import box_list +from object_detection.utils import tf_version + +_DEFAULT_NUM_CHANNELS = 3 +_DEFAULT_NUM_COORD_BOX = 4 + +if tf_version.is_tf1(): + from tensorflow.tools.graph_transforms import TransformGraph # pylint: disable=g-import-not-at-top + + +def get_const_center_size_encoded_anchors(anchors): + """Exports center-size encoded anchors as a constant tensor. + + Args: + anchors: a float32 tensor of shape [num_anchors, 4] containing the anchor + boxes + + Returns: + encoded_anchors: a float32 constant tensor of shape [num_anchors, 4] + containing the anchor boxes. + """ + anchor_boxlist = box_list.BoxList(anchors) + y, x, h, w = anchor_boxlist.get_center_coordinates_and_sizes() + num_anchors = y.get_shape().as_list() + + with tf.Session() as sess: + y_out, x_out, h_out, w_out = sess.run([y, x, h, w]) + encoded_anchors = tf.constant( + np.transpose(np.stack((y_out, x_out, h_out, w_out))), + dtype=tf.float32, + shape=[num_anchors[0], _DEFAULT_NUM_COORD_BOX], + name='anchors') + return encoded_anchors + + +def append_postprocessing_op(frozen_graph_def, + max_detections, + max_classes_per_detection, + nms_score_threshold, + nms_iou_threshold, + num_classes, + scale_values, + detections_per_class=100, + use_regular_nms=False, + additional_output_tensors=()): + """Appends postprocessing custom op. + + Args: + frozen_graph_def: Frozen GraphDef for SSD model after freezing the + checkpoint + max_detections: Maximum number of detections (boxes) to show + max_classes_per_detection: Number of classes to display per detection + nms_score_threshold: Score threshold used in Non-maximal suppression in + post-processing + nms_iou_threshold: Intersection-over-union threshold used in Non-maximal + suppression in post-processing + num_classes: number of classes in SSD detector + scale_values: scale values is a dict with following key-value pairs + {y_scale: 10, x_scale: 10, h_scale: 5, w_scale: 5} that are used in decode + centersize boxes + detections_per_class: In regular NonMaxSuppression, number of anchors used + for NonMaxSuppression per class + use_regular_nms: Flag to set postprocessing op to use Regular NMS instead of + Fast NMS. + additional_output_tensors: Array of additional tensor names to output. + Tensors are appended after postprocessing output. + + Returns: + transformed_graph_def: Frozen GraphDef with postprocessing custom op + appended + TFLite_Detection_PostProcess custom op node has four outputs: + detection_boxes: a float32 tensor of shape [1, num_boxes, 4] with box + locations + detection_classes: a float32 tensor of shape [1, num_boxes] + with class indices + detection_scores: a float32 tensor of shape [1, num_boxes] + with class scores + num_boxes: a float32 tensor of size 1 containing the number of detected + boxes + """ + new_output = frozen_graph_def.node.add() + new_output.op = 'TFLite_Detection_PostProcess' + new_output.name = 'TFLite_Detection_PostProcess' + new_output.attr['_output_quantized'].CopyFrom( + attr_value_pb2.AttrValue(b=True)) + new_output.attr['_output_types'].list.type.extend([ + types_pb2.DT_FLOAT, types_pb2.DT_FLOAT, types_pb2.DT_FLOAT, + types_pb2.DT_FLOAT + ]) + new_output.attr['_support_output_type_float_in_quantized_op'].CopyFrom( + attr_value_pb2.AttrValue(b=True)) + new_output.attr['max_detections'].CopyFrom( + attr_value_pb2.AttrValue(i=max_detections)) + new_output.attr['max_classes_per_detection'].CopyFrom( + attr_value_pb2.AttrValue(i=max_classes_per_detection)) + new_output.attr['nms_score_threshold'].CopyFrom( + attr_value_pb2.AttrValue(f=nms_score_threshold.pop())) + new_output.attr['nms_iou_threshold'].CopyFrom( + attr_value_pb2.AttrValue(f=nms_iou_threshold.pop())) + new_output.attr['num_classes'].CopyFrom( + attr_value_pb2.AttrValue(i=num_classes)) + + new_output.attr['y_scale'].CopyFrom( + attr_value_pb2.AttrValue(f=scale_values['y_scale'].pop())) + new_output.attr['x_scale'].CopyFrom( + attr_value_pb2.AttrValue(f=scale_values['x_scale'].pop())) + new_output.attr['h_scale'].CopyFrom( + attr_value_pb2.AttrValue(f=scale_values['h_scale'].pop())) + new_output.attr['w_scale'].CopyFrom( + attr_value_pb2.AttrValue(f=scale_values['w_scale'].pop())) + new_output.attr['detections_per_class'].CopyFrom( + attr_value_pb2.AttrValue(i=detections_per_class)) + new_output.attr['use_regular_nms'].CopyFrom( + attr_value_pb2.AttrValue(b=use_regular_nms)) + + new_output.input.extend( + ['raw_outputs/box_encodings', 'raw_outputs/class_predictions', 'anchors']) + # Transform the graph to append new postprocessing op + input_names = [] + output_names = ['TFLite_Detection_PostProcess' + ] + list(additional_output_tensors) + transforms = ['strip_unused_nodes'] + transformed_graph_def = TransformGraph(frozen_graph_def, input_names, + output_names, transforms) + return transformed_graph_def + + +def export_tflite_graph(pipeline_config, + trained_checkpoint_prefix, + output_dir, + add_postprocessing_op, + max_detections, + max_classes_per_detection, + detections_per_class=100, + use_regular_nms=False, + binary_graph_name='tflite_graph.pb', + txt_graph_name='tflite_graph.pbtxt', + additional_output_tensors=()): + """Exports a tflite compatible graph and anchors for ssd detection model. + + Anchors are written to a tensor and tflite compatible graph + is written to output_dir/tflite_graph.pb. + + Args: + pipeline_config: a pipeline.proto object containing the configuration for + SSD model to export. + trained_checkpoint_prefix: a file prefix for the checkpoint containing the + trained parameters of the SSD model. + output_dir: A directory to write the tflite graph and anchor file to. + add_postprocessing_op: If add_postprocessing_op is true: frozen graph adds a + TFLite_Detection_PostProcess custom op + max_detections: Maximum number of detections (boxes) to show + max_classes_per_detection: Number of classes to display per detection + detections_per_class: In regular NonMaxSuppression, number of anchors used + for NonMaxSuppression per class + use_regular_nms: Flag to set postprocessing op to use Regular NMS instead of + Fast NMS. + binary_graph_name: Name of the exported graph file in binary format. + txt_graph_name: Name of the exported graph file in text format. + additional_output_tensors: Array of additional tensor names to output. + Additional tensors are appended to the end of output tensor list. + + Raises: + ValueError: if the pipeline config contains models other than ssd or uses an + fixed_shape_resizer and provides a shape as well. + """ + tf.gfile.MakeDirs(output_dir) + if pipeline_config.model.WhichOneof('model') != 'ssd': + raise ValueError('Only ssd models are supported in tflite. ' + 'Found {} in config'.format( + pipeline_config.model.WhichOneof('model'))) + + num_classes = pipeline_config.model.ssd.num_classes + nms_score_threshold = { + pipeline_config.model.ssd.post_processing.batch_non_max_suppression + .score_threshold + } + nms_iou_threshold = { + pipeline_config.model.ssd.post_processing.batch_non_max_suppression + .iou_threshold + } + scale_values = {} + scale_values['y_scale'] = { + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale + } + scale_values['x_scale'] = { + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale + } + scale_values['h_scale'] = { + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale + } + scale_values['w_scale'] = { + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale + } + + image_resizer_config = pipeline_config.model.ssd.image_resizer + image_resizer = image_resizer_config.WhichOneof('image_resizer_oneof') + num_channels = _DEFAULT_NUM_CHANNELS + if image_resizer == 'fixed_shape_resizer': + height = image_resizer_config.fixed_shape_resizer.height + width = image_resizer_config.fixed_shape_resizer.width + if image_resizer_config.fixed_shape_resizer.convert_to_grayscale: + num_channels = 1 + shape = [1, height, width, num_channels] + else: + raise ValueError( + 'Only fixed_shape_resizer' + 'is supported with tflite. Found {}'.format( + image_resizer_config.WhichOneof('image_resizer_oneof'))) + + image = tf.placeholder( + tf.float32, shape=shape, name='normalized_input_image_tensor') + + detection_model = model_builder.build( + pipeline_config.model, is_training=False) + predicted_tensors = detection_model.predict(image, true_image_shapes=None) + # The score conversion occurs before the post-processing custom op + _, score_conversion_fn = post_processing_builder.build( + pipeline_config.model.ssd.post_processing) + class_predictions = score_conversion_fn( + predicted_tensors['class_predictions_with_background']) + + with tf.name_scope('raw_outputs'): + # 'raw_outputs/box_encodings': a float32 tensor of shape [1, num_anchors, 4] + # containing the encoded box predictions. Note that these are raw + # predictions and no Non-Max suppression is applied on them and + # no decode center size boxes is applied to them. + tf.identity(predicted_tensors['box_encodings'], name='box_encodings') + # 'raw_outputs/class_predictions': a float32 tensor of shape + # [1, num_anchors, num_classes] containing the class scores for each anchor + # after applying score conversion. + tf.identity(class_predictions, name='class_predictions') + # 'anchors': a float32 tensor of shape + # [4, num_anchors] containing the anchors as a constant node. + tf.identity( + get_const_center_size_encoded_anchors(predicted_tensors['anchors']), + name='anchors') + + # Add global step to the graph, so we know the training step number when we + # evaluate the model. + tf.train.get_or_create_global_step() + + # graph rewriter + is_quantized = pipeline_config.HasField('graph_rewriter') + if is_quantized: + graph_rewriter_config = pipeline_config.graph_rewriter + graph_rewriter_fn = graph_rewriter_builder.build( + graph_rewriter_config, is_training=False) + graph_rewriter_fn() + + if pipeline_config.model.ssd.feature_extractor.HasField('fpn'): + exporter.rewrite_nn_resize_op(is_quantized) + + # freeze the graph + saver_kwargs = {} + if pipeline_config.eval_config.use_moving_averages: + saver_kwargs['write_version'] = saver_pb2.SaverDef.V1 + moving_average_checkpoint = tempfile.NamedTemporaryFile() + exporter.replace_variable_values_with_moving_averages( + tf.get_default_graph(), trained_checkpoint_prefix, + moving_average_checkpoint.name) + checkpoint_to_use = moving_average_checkpoint.name + else: + checkpoint_to_use = trained_checkpoint_prefix + + saver = tf.train.Saver(**saver_kwargs) + input_saver_def = saver.as_saver_def() + frozen_graph_def = exporter.freeze_graph_with_def_protos( + input_graph_def=tf.get_default_graph().as_graph_def(), + input_saver_def=input_saver_def, + input_checkpoint=checkpoint_to_use, + output_node_names=','.join([ + 'raw_outputs/box_encodings', 'raw_outputs/class_predictions', + 'anchors' + ] + list(additional_output_tensors)), + restore_op_name='save/restore_all', + filename_tensor_name='save/Const:0', + clear_devices=True, + output_graph='', + initializer_nodes='') + + # Add new operation to do post processing in a custom op (TF Lite only) + if add_postprocessing_op: + transformed_graph_def = append_postprocessing_op( + frozen_graph_def, + max_detections, + max_classes_per_detection, + nms_score_threshold, + nms_iou_threshold, + num_classes, + scale_values, + detections_per_class, + use_regular_nms, + additional_output_tensors=additional_output_tensors) + else: + # Return frozen without adding post-processing custom op + transformed_graph_def = frozen_graph_def + + binary_graph = os.path.join(output_dir, binary_graph_name) + with tf.gfile.GFile(binary_graph, 'wb') as f: + f.write(transformed_graph_def.SerializeToString()) + txt_graph = os.path.join(output_dir, txt_graph_name) + with tf.gfile.GFile(txt_graph, 'w') as f: + f.write(str(transformed_graph_def)) diff --git a/workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib_tf1_test.py b/workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib_tf1_test.py new file mode 100644 index 0000000..3625b9f --- /dev/null +++ b/workspace/virtuallab/object_detection/export_tflite_ssd_graph_lib_tf1_test.py @@ -0,0 +1,426 @@ +# Lint as: python2, python3 +# Copyright 2018 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== +"""Tests for object_detection.export_tflite_ssd_graph.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import os +import unittest +import numpy as np +import six +import tensorflow.compat.v1 as tf +import tf_slim as slim + +from tensorflow.core.framework import types_pb2 +from object_detection import export_tflite_ssd_graph_lib +from object_detection import exporter +from object_detection.builders import graph_rewriter_builder +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.protos import graph_rewriter_pb2 +from object_detection.protos import pipeline_pb2 +from object_detection.protos import post_processing_pb2 +from object_detection.utils import tf_version + +# pylint: disable=g-import-not-at-top + +if six.PY2: + import mock +else: + from unittest import mock # pylint: disable=g-importing-member +# pylint: enable=g-import-not-at-top + + +class FakeModel(model.DetectionModel): + + def __init__(self, add_detection_masks=False): + self._add_detection_masks = add_detection_masks + + def preprocess(self, inputs): + pass + + def predict(self, preprocessed_inputs, true_image_shapes): + features = slim.conv2d(preprocessed_inputs, 3, 1) + with tf.control_dependencies([features]): + prediction_tensors = { + 'box_encodings': + tf.constant([[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]], + tf.float32), + 'class_predictions_with_background': + tf.constant([[[0.7, 0.6], [0.9, 0.0]]], tf.float32), + } + with tf.control_dependencies( + [tf.convert_to_tensor(features.get_shape().as_list()[1:3])]): + prediction_tensors['anchors'] = tf.constant( + [[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 1.0, 1.0]], tf.float32) + return prediction_tensors + + def postprocess(self, prediction_tensors, true_image_shapes): + pass + + def restore_map(self, checkpoint_path, from_detection_checkpoint): + pass + + def restore_from_objects(self, fine_tune_checkpoint_type): + pass + + def loss(self, prediction_dict, true_image_shapes): + pass + + def regularization_losses(self): + pass + + def updates(self): + pass + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only test.') +class ExportTfliteGraphTest(tf.test.TestCase): + + def _save_checkpoint_from_mock_model(self, + checkpoint_path, + use_moving_averages, + quantize=False, + num_channels=3): + g = tf.Graph() + with g.as_default(): + mock_model = FakeModel() + inputs = tf.placeholder(tf.float32, shape=[1, 10, 10, num_channels]) + mock_model.predict(inputs, true_image_shapes=None) + if use_moving_averages: + tf.train.ExponentialMovingAverage(0.0).apply() + tf.train.get_or_create_global_step() + if quantize: + graph_rewriter_config = graph_rewriter_pb2.GraphRewriter() + graph_rewriter_config.quantization.delay = 500000 + graph_rewriter_fn = graph_rewriter_builder.build( + graph_rewriter_config, is_training=False) + graph_rewriter_fn() + + saver = tf.train.Saver() + init = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init) + saver.save(sess, checkpoint_path) + + def _assert_quant_vars_exists(self, tflite_graph_file): + with tf.gfile.Open(tflite_graph_file, mode='rb') as f: + graph_string = f.read() + print(graph_string) + self.assertIn(six.ensure_binary('quant'), graph_string) + + def _import_graph_and_run_inference(self, tflite_graph_file, num_channels=3): + """Imports a tflite graph, runs single inference and returns outputs.""" + graph = tf.Graph() + with graph.as_default(): + graph_def = tf.GraphDef() + with tf.gfile.Open(tflite_graph_file, mode='rb') as f: + graph_def.ParseFromString(f.read()) + tf.import_graph_def(graph_def, name='') + input_tensor = graph.get_tensor_by_name('normalized_input_image_tensor:0') + box_encodings = graph.get_tensor_by_name('raw_outputs/box_encodings:0') + class_predictions = graph.get_tensor_by_name( + 'raw_outputs/class_predictions:0') + with self.test_session(graph) as sess: + [box_encodings_np, class_predictions_np] = sess.run( + [box_encodings, class_predictions], + feed_dict={input_tensor: np.random.rand(1, 10, 10, num_channels)}) + return box_encodings_np, class_predictions_np + + def _export_graph(self, + pipeline_config, + num_channels=3, + additional_output_tensors=()): + """Exports a tflite graph.""" + output_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(output_dir, 'model.ckpt') + tflite_graph_file = os.path.join(output_dir, 'tflite_graph.pb') + + quantize = pipeline_config.HasField('graph_rewriter') + self._save_checkpoint_from_mock_model( + trained_checkpoint_prefix, + use_moving_averages=pipeline_config.eval_config.use_moving_averages, + quantize=quantize, + num_channels=num_channels) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + + with tf.Graph().as_default(): + tf.identity( + tf.constant([[1, 2], [3, 4]], tf.uint8), name='UnattachedTensor') + export_tflite_ssd_graph_lib.export_tflite_graph( + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_dir=output_dir, + add_postprocessing_op=False, + max_detections=10, + max_classes_per_detection=1, + additional_output_tensors=additional_output_tensors) + return tflite_graph_file + + def _export_graph_with_postprocessing_op(self, + pipeline_config, + num_channels=3, + additional_output_tensors=()): + """Exports a tflite graph with custom postprocessing op.""" + output_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(output_dir, 'model.ckpt') + tflite_graph_file = os.path.join(output_dir, 'tflite_graph.pb') + + quantize = pipeline_config.HasField('graph_rewriter') + self._save_checkpoint_from_mock_model( + trained_checkpoint_prefix, + use_moving_averages=pipeline_config.eval_config.use_moving_averages, + quantize=quantize, + num_channels=num_channels) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + + with tf.Graph().as_default(): + tf.identity( + tf.constant([[1, 2], [3, 4]], tf.uint8), name='UnattachedTensor') + export_tflite_ssd_graph_lib.export_tflite_graph( + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_dir=output_dir, + add_postprocessing_op=True, + max_detections=10, + max_classes_per_detection=1, + additional_output_tensors=additional_output_tensors) + return tflite_graph_file + + def test_export_tflite_graph_with_moving_averages(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = True + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph(pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + + (box_encodings_np, class_predictions_np + ) = self._import_graph_and_run_inference(tflite_graph_file) + self.assertAllClose(box_encodings_np, + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]]) + self.assertAllClose(class_predictions_np, [[[0.7, 0.6], [0.9, 0.0]]]) + + def test_export_tflite_graph_without_moving_averages(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph(pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + (box_encodings_np, class_predictions_np + ) = self._import_graph_and_run_inference(tflite_graph_file) + self.assertAllClose(box_encodings_np, + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]]) + self.assertAllClose(class_predictions_np, [[[0.7, 0.6], [0.9, 0.0]]]) + + def test_export_tflite_graph_grayscale(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + (pipeline_config.model.ssd.image_resizer.fixed_shape_resizer + ).convert_to_grayscale = True + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph(pipeline_config, num_channels=1) + self.assertTrue(os.path.exists(tflite_graph_file)) + (box_encodings_np, + class_predictions_np) = self._import_graph_and_run_inference( + tflite_graph_file, num_channels=1) + self.assertAllClose(box_encodings_np, + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]]) + self.assertAllClose(class_predictions_np, [[[0.7, 0.6], [0.9, 0.0]]]) + + def test_export_tflite_graph_with_quantization(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.graph_rewriter.quantization.delay = 500000 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph(pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + self._assert_quant_vars_exists(tflite_graph_file) + (box_encodings_np, class_predictions_np + ) = self._import_graph_and_run_inference(tflite_graph_file) + self.assertAllClose(box_encodings_np, + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]]) + self.assertAllClose(class_predictions_np, [[[0.7, 0.6], [0.9, 0.0]]]) + + def test_export_tflite_graph_with_softmax_score_conversion(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.post_processing.score_converter = ( + post_processing_pb2.PostProcessing.SOFTMAX) + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph(pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + (box_encodings_np, class_predictions_np + ) = self._import_graph_and_run_inference(tflite_graph_file) + self.assertAllClose(box_encodings_np, + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]]) + self.assertAllClose(class_predictions_np, + [[[0.524979, 0.475021], [0.710949, 0.28905]]]) + + def test_export_tflite_graph_with_sigmoid_score_conversion(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.post_processing.score_converter = ( + post_processing_pb2.PostProcessing.SIGMOID) + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph(pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + (box_encodings_np, class_predictions_np + ) = self._import_graph_and_run_inference(tflite_graph_file) + self.assertAllClose(box_encodings_np, + [[[0.0, 0.0, 0.5, 0.5], [0.5, 0.5, 0.8, 0.8]]]) + self.assertAllClose(class_predictions_np, + [[[0.668188, 0.645656], [0.710949, 0.5]]]) + + def test_export_tflite_graph_with_postprocessing_op(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.post_processing.score_converter = ( + post_processing_pb2.PostProcessing.SIGMOID) + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.y_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.x_scale = 10.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.height_scale = 5.0 + pipeline_config.model.ssd.box_coder.faster_rcnn_box_coder.width_scale = 5.0 + tflite_graph_file = self._export_graph_with_postprocessing_op( + pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + graph = tf.Graph() + with graph.as_default(): + graph_def = tf.GraphDef() + with tf.gfile.Open(tflite_graph_file, mode='rb') as f: + graph_def.ParseFromString(f.read()) + all_op_names = [node.name for node in graph_def.node] + self.assertIn('TFLite_Detection_PostProcess', all_op_names) + self.assertNotIn('UnattachedTensor', all_op_names) + for node in graph_def.node: + if node.name == 'TFLite_Detection_PostProcess': + self.assertTrue(node.attr['_output_quantized'].b) + self.assertTrue( + node.attr['_support_output_type_float_in_quantized_op'].b) + self.assertEqual(node.attr['y_scale'].f, 10.0) + self.assertEqual(node.attr['x_scale'].f, 10.0) + self.assertEqual(node.attr['h_scale'].f, 5.0) + self.assertEqual(node.attr['w_scale'].f, 5.0) + self.assertEqual(node.attr['num_classes'].i, 2) + self.assertTrue( + all([ + t == types_pb2.DT_FLOAT + for t in node.attr['_output_types'].list.type + ])) + + def test_export_tflite_graph_with_additional_tensors(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + tflite_graph_file = self._export_graph( + pipeline_config, additional_output_tensors=['UnattachedTensor']) + self.assertTrue(os.path.exists(tflite_graph_file)) + graph = tf.Graph() + with graph.as_default(): + graph_def = tf.GraphDef() + with tf.gfile.Open(tflite_graph_file, mode='rb') as f: + graph_def.ParseFromString(f.read()) + all_op_names = [node.name for node in graph_def.node] + self.assertIn('UnattachedTensor', all_op_names) + + def test_export_tflite_graph_with_postprocess_op_and_additional_tensors(self): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + pipeline_config.model.ssd.post_processing.score_converter = ( + post_processing_pb2.PostProcessing.SIGMOID) + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.num_classes = 2 + tflite_graph_file = self._export_graph_with_postprocessing_op( + pipeline_config, additional_output_tensors=['UnattachedTensor']) + self.assertTrue(os.path.exists(tflite_graph_file)) + graph = tf.Graph() + with graph.as_default(): + graph_def = tf.GraphDef() + with tf.gfile.Open(tflite_graph_file, mode='rb') as f: + graph_def.ParseFromString(f.read()) + all_op_names = [node.name for node in graph_def.node] + self.assertIn('TFLite_Detection_PostProcess', all_op_names) + self.assertIn('UnattachedTensor', all_op_names) + + @mock.patch.object(exporter, 'rewrite_nn_resize_op') + def test_export_with_nn_resize_op_not_called_without_fpn(self, mock_get): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + tflite_graph_file = self._export_graph_with_postprocessing_op( + pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + mock_get.assert_not_called() + + @mock.patch.object(exporter, 'rewrite_nn_resize_op') + def test_export_with_nn_resize_op_called_with_fpn(self, mock_get): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.height = 10 + pipeline_config.model.ssd.image_resizer.fixed_shape_resizer.width = 10 + pipeline_config.model.ssd.feature_extractor.fpn.min_level = 3 + pipeline_config.model.ssd.feature_extractor.fpn.max_level = 7 + tflite_graph_file = self._export_graph_with_postprocessing_op( + pipeline_config) + self.assertTrue(os.path.exists(tflite_graph_file)) + self.assertEqual(1, mock_get.call_count) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/exporter.py b/workspace/virtuallab/object_detection/exporter.py new file mode 100644 index 0000000..61c5f7f --- /dev/null +++ b/workspace/virtuallab/object_detection/exporter.py @@ -0,0 +1,656 @@ +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Functions to export object detection inference graph.""" +import os +import tempfile +import tensorflow.compat.v1 as tf +import tf_slim as slim +from tensorflow.core.protobuf import saver_pb2 +from tensorflow.python.tools import freeze_graph # pylint: disable=g-direct-tensorflow-import +from object_detection.builders import graph_rewriter_builder +from object_detection.builders import model_builder +from object_detection.core import standard_fields as fields +from object_detection.data_decoders import tf_example_decoder +from object_detection.utils import config_util +from object_detection.utils import shape_utils + +# pylint: disable=g-import-not-at-top +try: + from tensorflow.contrib import tfprof as contrib_tfprof + from tensorflow.contrib.quantize.python import graph_matcher +except ImportError: + # TF 2.0 doesn't ship with contrib. + pass +# pylint: enable=g-import-not-at-top + +freeze_graph_with_def_protos = freeze_graph.freeze_graph_with_def_protos + + +def parse_side_inputs(side_input_shapes_string, side_input_names_string, + side_input_types_string): + """Parses side input flags. + + Args: + side_input_shapes_string: The shape of the side input tensors, provided as a + comma-separated list of integers. A value of -1 is used for unknown + dimensions. A `/` denotes a break, starting the shape of the next side + input tensor. + side_input_names_string: The names of the side input tensors, provided as a + comma-separated list of strings. + side_input_types_string: The type of the side input tensors, provided as a + comma-separated list of types, each of `string`, `integer`, or `float`. + + Returns: + side_input_shapes: A list of shapes. + side_input_names: A list of strings. + side_input_types: A list of tensorflow dtypes. + + """ + if side_input_shapes_string: + side_input_shapes = [] + for side_input_shape_list in side_input_shapes_string.split('/'): + side_input_shape = [ + int(dim) if dim != '-1' else None + for dim in side_input_shape_list.split(',') + ] + side_input_shapes.append(side_input_shape) + else: + raise ValueError('When using side_inputs, side_input_shapes must be ' + 'specified in the input flags.') + if side_input_names_string: + side_input_names = list(side_input_names_string.split(',')) + else: + raise ValueError('When using side_inputs, side_input_names must be ' + 'specified in the input flags.') + if side_input_types_string: + typelookup = {'float': tf.float32, 'int': tf.int32, 'string': tf.string} + side_input_types = [ + typelookup[side_input_type] + for side_input_type in side_input_types_string.split(',') + ] + else: + raise ValueError('When using side_inputs, side_input_types must be ' + 'specified in the input flags.') + return side_input_shapes, side_input_names, side_input_types + + +def rewrite_nn_resize_op(is_quantized=False): + """Replaces a custom nearest-neighbor resize op with the Tensorflow version. + + Some graphs use this custom version for TPU-compatibility. + + Args: + is_quantized: True if the default graph is quantized. + """ + def remove_nn(): + """Remove nearest neighbor upsampling structures and replace with TF op.""" + input_pattern = graph_matcher.OpTypePattern( + 'FakeQuantWithMinMaxVars' if is_quantized else '*') + stack_1_pattern = graph_matcher.OpTypePattern( + 'Pack', inputs=[input_pattern, input_pattern], ordered_inputs=False) + stack_2_pattern = graph_matcher.OpTypePattern( + 'Pack', inputs=[stack_1_pattern, stack_1_pattern], ordered_inputs=False) + reshape_pattern = graph_matcher.OpTypePattern( + 'Reshape', inputs=[stack_2_pattern, 'Const'], ordered_inputs=False) + consumer_pattern1 = graph_matcher.OpTypePattern( + 'Add|AddV2|Max|Mul', inputs=[reshape_pattern, '*'], + ordered_inputs=False) + consumer_pattern2 = graph_matcher.OpTypePattern( + 'StridedSlice', inputs=[reshape_pattern, '*', '*', '*'], + ordered_inputs=False) + + def replace_matches(consumer_pattern): + """Search for nearest neighbor pattern and replace with TF op.""" + match_counter = 0 + matcher = graph_matcher.GraphMatcher(consumer_pattern) + for match in matcher.match_graph(tf.get_default_graph()): + match_counter += 1 + projection_op = match.get_op(input_pattern) + reshape_op = match.get_op(reshape_pattern) + consumer_op = match.get_op(consumer_pattern) + nn_resize = tf.image.resize_nearest_neighbor( + projection_op.outputs[0], + reshape_op.outputs[0].shape.dims[1:3], + align_corners=False, + name=os.path.split(reshape_op.name)[0] + '/resize_nearest_neighbor') + + for index, op_input in enumerate(consumer_op.inputs): + if op_input == reshape_op.outputs[0]: + consumer_op._update_input(index, nn_resize) # pylint: disable=protected-access + break + + return match_counter + + match_counter = replace_matches(consumer_pattern1) + match_counter += replace_matches(consumer_pattern2) + + tf.logging.info('Found and fixed {} matches'.format(match_counter)) + return match_counter + + # Applying twice because both inputs to Add could be NN pattern + total_removals = 0 + while remove_nn(): + total_removals += 1 + # This number is chosen based on the nas-fpn architecture. + if total_removals > 4: + raise ValueError('Graph removal encountered a infinite loop.') + + +def replace_variable_values_with_moving_averages(graph, + current_checkpoint_file, + new_checkpoint_file, + no_ema_collection=None): + """Replaces variable values in the checkpoint with their moving averages. + + If the current checkpoint has shadow variables maintaining moving averages of + the variables defined in the graph, this function generates a new checkpoint + where the variables contain the values of their moving averages. + + Args: + graph: a tf.Graph object. + current_checkpoint_file: a checkpoint containing both original variables and + their moving averages. + new_checkpoint_file: file path to write a new checkpoint. + no_ema_collection: A list of namescope substrings to match the variables + to eliminate EMA. + """ + with graph.as_default(): + variable_averages = tf.train.ExponentialMovingAverage(0.0) + ema_variables_to_restore = variable_averages.variables_to_restore() + ema_variables_to_restore = config_util.remove_unecessary_ema( + ema_variables_to_restore, no_ema_collection) + with tf.Session() as sess: + read_saver = tf.train.Saver(ema_variables_to_restore) + read_saver.restore(sess, current_checkpoint_file) + write_saver = tf.train.Saver() + write_saver.save(sess, new_checkpoint_file) + + +def _image_tensor_input_placeholder(input_shape=None): + """Returns input placeholder and a 4-D uint8 image tensor.""" + if input_shape is None: + input_shape = (None, None, None, 3) + input_tensor = tf.placeholder( + dtype=tf.uint8, shape=input_shape, name='image_tensor') + return input_tensor, input_tensor + + +def _side_input_tensor_placeholder(side_input_shape, side_input_name, + side_input_type): + """Returns side input placeholder and side input tensor.""" + side_input_tensor = tf.placeholder( + dtype=side_input_type, shape=side_input_shape, name=side_input_name) + return side_input_tensor, side_input_tensor + + +def _tf_example_input_placeholder(input_shape=None): + """Returns input that accepts a batch of strings with tf examples. + + Args: + input_shape: the shape to resize the output decoded images to (optional). + + Returns: + a tuple of input placeholder and the output decoded images. + """ + batch_tf_example_placeholder = tf.placeholder( + tf.string, shape=[None], name='tf_example') + def decode(tf_example_string_tensor): + tensor_dict = tf_example_decoder.TfExampleDecoder().decode( + tf_example_string_tensor) + image_tensor = tensor_dict[fields.InputDataFields.image] + if input_shape is not None: + image_tensor = tf.image.resize(image_tensor, input_shape[1:3]) + return image_tensor + return (batch_tf_example_placeholder, + shape_utils.static_or_dynamic_map_fn( + decode, + elems=batch_tf_example_placeholder, + dtype=tf.uint8, + parallel_iterations=32, + back_prop=False)) + + +def _encoded_image_string_tensor_input_placeholder(input_shape=None): + """Returns input that accepts a batch of PNG or JPEG strings. + + Args: + input_shape: the shape to resize the output decoded images to (optional). + + Returns: + a tuple of input placeholder and the output decoded images. + """ + batch_image_str_placeholder = tf.placeholder( + dtype=tf.string, + shape=[None], + name='encoded_image_string_tensor') + def decode(encoded_image_string_tensor): + image_tensor = tf.image.decode_image(encoded_image_string_tensor, + channels=3) + image_tensor.set_shape((None, None, 3)) + if input_shape is not None: + image_tensor = tf.image.resize(image_tensor, input_shape[1:3]) + return image_tensor + return (batch_image_str_placeholder, + tf.map_fn( + decode, + elems=batch_image_str_placeholder, + dtype=tf.uint8, + parallel_iterations=32, + back_prop=False)) + + +input_placeholder_fn_map = { + 'image_tensor': _image_tensor_input_placeholder, + 'encoded_image_string_tensor': + _encoded_image_string_tensor_input_placeholder, + 'tf_example': _tf_example_input_placeholder +} + + +def add_output_tensor_nodes(postprocessed_tensors, + output_collection_name='inference_op'): + """Adds output nodes for detection boxes and scores. + + Adds the following nodes for output tensors - + * num_detections: float32 tensor of shape [batch_size]. + * detection_boxes: float32 tensor of shape [batch_size, num_boxes, 4] + containing detected boxes. + * detection_scores: float32 tensor of shape [batch_size, num_boxes] + containing scores for the detected boxes. + * detection_multiclass_scores: (Optional) float32 tensor of shape + [batch_size, num_boxes, num_classes_with_background] for containing class + score distribution for detected boxes including background if any. + * detection_features: (Optional) float32 tensor of shape + [batch, num_boxes, roi_height, roi_width, depth] + containing classifier features + for each detected box + * detection_classes: float32 tensor of shape [batch_size, num_boxes] + containing class predictions for the detected boxes. + * detection_keypoints: (Optional) float32 tensor of shape + [batch_size, num_boxes, num_keypoints, 2] containing keypoints for each + detection box. + * detection_masks: (Optional) float32 tensor of shape + [batch_size, num_boxes, mask_height, mask_width] containing masks for each + detection box. + + Args: + postprocessed_tensors: a dictionary containing the following fields + 'detection_boxes': [batch, max_detections, 4] + 'detection_scores': [batch, max_detections] + 'detection_multiclass_scores': [batch, max_detections, + num_classes_with_background] + 'detection_features': [batch, num_boxes, roi_height, roi_width, depth] + 'detection_classes': [batch, max_detections] + 'detection_masks': [batch, max_detections, mask_height, mask_width] + (optional). + 'detection_keypoints': [batch, max_detections, num_keypoints, 2] + (optional). + 'num_detections': [batch] + output_collection_name: Name of collection to add output tensors to. + + Returns: + A tensor dict containing the added output tensor nodes. + """ + detection_fields = fields.DetectionResultFields + label_id_offset = 1 + boxes = postprocessed_tensors.get(detection_fields.detection_boxes) + scores = postprocessed_tensors.get(detection_fields.detection_scores) + multiclass_scores = postprocessed_tensors.get( + detection_fields.detection_multiclass_scores) + box_classifier_features = postprocessed_tensors.get( + detection_fields.detection_features) + raw_boxes = postprocessed_tensors.get(detection_fields.raw_detection_boxes) + raw_scores = postprocessed_tensors.get(detection_fields.raw_detection_scores) + classes = postprocessed_tensors.get( + detection_fields.detection_classes) + label_id_offset + keypoints = postprocessed_tensors.get(detection_fields.detection_keypoints) + masks = postprocessed_tensors.get(detection_fields.detection_masks) + num_detections = postprocessed_tensors.get(detection_fields.num_detections) + outputs = {} + outputs[detection_fields.detection_boxes] = tf.identity( + boxes, name=detection_fields.detection_boxes) + outputs[detection_fields.detection_scores] = tf.identity( + scores, name=detection_fields.detection_scores) + if multiclass_scores is not None: + outputs[detection_fields.detection_multiclass_scores] = tf.identity( + multiclass_scores, name=detection_fields.detection_multiclass_scores) + if box_classifier_features is not None: + outputs[detection_fields.detection_features] = tf.identity( + box_classifier_features, + name=detection_fields.detection_features) + outputs[detection_fields.detection_classes] = tf.identity( + classes, name=detection_fields.detection_classes) + outputs[detection_fields.num_detections] = tf.identity( + num_detections, name=detection_fields.num_detections) + if raw_boxes is not None: + outputs[detection_fields.raw_detection_boxes] = tf.identity( + raw_boxes, name=detection_fields.raw_detection_boxes) + if raw_scores is not None: + outputs[detection_fields.raw_detection_scores] = tf.identity( + raw_scores, name=detection_fields.raw_detection_scores) + if keypoints is not None: + outputs[detection_fields.detection_keypoints] = tf.identity( + keypoints, name=detection_fields.detection_keypoints) + if masks is not None: + outputs[detection_fields.detection_masks] = tf.identity( + masks, name=detection_fields.detection_masks) + for output_key in outputs: + tf.add_to_collection(output_collection_name, outputs[output_key]) + + return outputs + + +def write_saved_model(saved_model_path, + frozen_graph_def, + inputs, + outputs): + """Writes SavedModel to disk. + + If checkpoint_path is not None bakes the weights into the graph thereby + eliminating the need of checkpoint files during inference. If the model + was trained with moving averages, setting use_moving_averages to true + restores the moving averages, otherwise the original set of variables + is restored. + + Args: + saved_model_path: Path to write SavedModel. + frozen_graph_def: tf.GraphDef holding frozen graph. + inputs: A tensor dictionary containing the inputs to a DetectionModel. + outputs: A tensor dictionary containing the outputs of a DetectionModel. + """ + with tf.Graph().as_default(): + with tf.Session() as sess: + + tf.import_graph_def(frozen_graph_def, name='') + + builder = tf.saved_model.builder.SavedModelBuilder(saved_model_path) + + tensor_info_inputs = {} + if isinstance(inputs, dict): + for k, v in inputs.items(): + tensor_info_inputs[k] = tf.saved_model.utils.build_tensor_info(v) + else: + tensor_info_inputs['inputs'] = tf.saved_model.utils.build_tensor_info( + inputs) + tensor_info_outputs = {} + for k, v in outputs.items(): + tensor_info_outputs[k] = tf.saved_model.utils.build_tensor_info(v) + + detection_signature = ( + tf.saved_model.signature_def_utils.build_signature_def( + inputs=tensor_info_inputs, + outputs=tensor_info_outputs, + method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME + )) + + builder.add_meta_graph_and_variables( + sess, + [tf.saved_model.tag_constants.SERVING], + signature_def_map={ + tf.saved_model.signature_constants + .DEFAULT_SERVING_SIGNATURE_DEF_KEY: + detection_signature, + }, + ) + builder.save() + + +def write_graph_and_checkpoint(inference_graph_def, + model_path, + input_saver_def, + trained_checkpoint_prefix): + """Writes the graph and the checkpoint into disk.""" + for node in inference_graph_def.node: + node.device = '' + with tf.Graph().as_default(): + tf.import_graph_def(inference_graph_def, name='') + with tf.Session() as sess: + saver = tf.train.Saver( + saver_def=input_saver_def, save_relative_paths=True) + saver.restore(sess, trained_checkpoint_prefix) + saver.save(sess, model_path) + + +def _get_outputs_from_inputs(input_tensors, detection_model, + output_collection_name, **side_inputs): + inputs = tf.cast(input_tensors, dtype=tf.float32) + preprocessed_inputs, true_image_shapes = detection_model.preprocess(inputs) + output_tensors = detection_model.predict( + preprocessed_inputs, true_image_shapes, **side_inputs) + postprocessed_tensors = detection_model.postprocess( + output_tensors, true_image_shapes) + return add_output_tensor_nodes(postprocessed_tensors, + output_collection_name) + + +def build_detection_graph(input_type, detection_model, input_shape, + output_collection_name, graph_hook_fn, + use_side_inputs=False, side_input_shapes=None, + side_input_names=None, side_input_types=None): + """Build the detection graph.""" + if input_type not in input_placeholder_fn_map: + raise ValueError('Unknown input type: {}'.format(input_type)) + placeholder_args = {} + side_inputs = {} + if input_shape is not None: + if (input_type != 'image_tensor' and + input_type != 'encoded_image_string_tensor' and + input_type != 'tf_example' and + input_type != 'tf_sequence_example'): + raise ValueError('Can only specify input shape for `image_tensor`, ' + '`encoded_image_string_tensor`, `tf_example`, ' + ' or `tf_sequence_example` inputs.') + placeholder_args['input_shape'] = input_shape + placeholder_tensor, input_tensors = input_placeholder_fn_map[input_type]( + **placeholder_args) + placeholder_tensors = {'inputs': placeholder_tensor} + if use_side_inputs: + for idx, side_input_name in enumerate(side_input_names): + side_input_placeholder, side_input = _side_input_tensor_placeholder( + side_input_shapes[idx], side_input_name, side_input_types[idx]) + print(side_input) + side_inputs[side_input_name] = side_input + placeholder_tensors[side_input_name] = side_input_placeholder + outputs = _get_outputs_from_inputs( + input_tensors=input_tensors, + detection_model=detection_model, + output_collection_name=output_collection_name, + **side_inputs) + + # Add global step to the graph. + slim.get_or_create_global_step() + + if graph_hook_fn: graph_hook_fn() + + return outputs, placeholder_tensors + + +def _export_inference_graph(input_type, + detection_model, + use_moving_averages, + trained_checkpoint_prefix, + output_directory, + additional_output_tensor_names=None, + input_shape=None, + output_collection_name='inference_op', + graph_hook_fn=None, + write_inference_graph=False, + temp_checkpoint_prefix='', + use_side_inputs=False, + side_input_shapes=None, + side_input_names=None, + side_input_types=None): + """Export helper.""" + tf.gfile.MakeDirs(output_directory) + frozen_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + saved_model_path = os.path.join(output_directory, 'saved_model') + model_path = os.path.join(output_directory, 'model.ckpt') + + outputs, placeholder_tensor_dict = build_detection_graph( + input_type=input_type, + detection_model=detection_model, + input_shape=input_shape, + output_collection_name=output_collection_name, + graph_hook_fn=graph_hook_fn, + use_side_inputs=use_side_inputs, + side_input_shapes=side_input_shapes, + side_input_names=side_input_names, + side_input_types=side_input_types) + + profile_inference_graph(tf.get_default_graph()) + saver_kwargs = {} + if use_moving_averages: + if not temp_checkpoint_prefix: + # This check is to be compatible with both version of SaverDef. + if os.path.isfile(trained_checkpoint_prefix): + saver_kwargs['write_version'] = saver_pb2.SaverDef.V1 + temp_checkpoint_prefix = tempfile.NamedTemporaryFile().name + else: + temp_checkpoint_prefix = tempfile.mkdtemp() + replace_variable_values_with_moving_averages( + tf.get_default_graph(), trained_checkpoint_prefix, + temp_checkpoint_prefix) + checkpoint_to_use = temp_checkpoint_prefix + else: + checkpoint_to_use = trained_checkpoint_prefix + + saver = tf.train.Saver(**saver_kwargs) + input_saver_def = saver.as_saver_def() + + write_graph_and_checkpoint( + inference_graph_def=tf.get_default_graph().as_graph_def(), + model_path=model_path, + input_saver_def=input_saver_def, + trained_checkpoint_prefix=checkpoint_to_use) + if write_inference_graph: + inference_graph_def = tf.get_default_graph().as_graph_def() + inference_graph_path = os.path.join(output_directory, + 'inference_graph.pbtxt') + for node in inference_graph_def.node: + node.device = '' + with tf.gfile.GFile(inference_graph_path, 'wb') as f: + f.write(str(inference_graph_def)) + + if additional_output_tensor_names is not None: + output_node_names = ','.join(list(outputs.keys())+( + additional_output_tensor_names)) + else: + output_node_names = ','.join(outputs.keys()) + + frozen_graph_def = freeze_graph.freeze_graph_with_def_protos( + input_graph_def=tf.get_default_graph().as_graph_def(), + input_saver_def=input_saver_def, + input_checkpoint=checkpoint_to_use, + output_node_names=output_node_names, + restore_op_name='save/restore_all', + filename_tensor_name='save/Const:0', + output_graph=frozen_graph_path, + clear_devices=True, + initializer_nodes='') + + write_saved_model(saved_model_path, frozen_graph_def, + placeholder_tensor_dict, outputs) + + +def export_inference_graph(input_type, + pipeline_config, + trained_checkpoint_prefix, + output_directory, + input_shape=None, + output_collection_name='inference_op', + additional_output_tensor_names=None, + write_inference_graph=False, + use_side_inputs=False, + side_input_shapes=None, + side_input_names=None, + side_input_types=None): + """Exports inference graph for the model specified in the pipeline config. + + Args: + input_type: Type of input for the graph. Can be one of ['image_tensor', + 'encoded_image_string_tensor', 'tf_example']. + pipeline_config: pipeline_pb2.TrainAndEvalPipelineConfig proto. + trained_checkpoint_prefix: Path to the trained checkpoint file. + output_directory: Path to write outputs. + input_shape: Sets a fixed shape for an `image_tensor` input. If not + specified, will default to [None, None, None, 3]. + output_collection_name: Name of collection to add output tensors to. + If None, does not add output tensors to a collection. + additional_output_tensor_names: list of additional output + tensors to include in the frozen graph. + write_inference_graph: If true, writes inference graph to disk. + use_side_inputs: If True, the model requires side_inputs. + side_input_shapes: List of shapes of the side input tensors, + required if use_side_inputs is True. + side_input_names: List of names of the side input tensors, + required if use_side_inputs is True. + side_input_types: List of types of the side input tensors, + required if use_side_inputs is True. + """ + detection_model = model_builder.build(pipeline_config.model, + is_training=False) + graph_rewriter_fn = None + if pipeline_config.HasField('graph_rewriter'): + graph_rewriter_config = pipeline_config.graph_rewriter + graph_rewriter_fn = graph_rewriter_builder.build(graph_rewriter_config, + is_training=False) + _export_inference_graph( + input_type, + detection_model, + pipeline_config.eval_config.use_moving_averages, + trained_checkpoint_prefix, + output_directory, + additional_output_tensor_names, + input_shape, + output_collection_name, + graph_hook_fn=graph_rewriter_fn, + write_inference_graph=write_inference_graph, + use_side_inputs=use_side_inputs, + side_input_shapes=side_input_shapes, + side_input_names=side_input_names, + side_input_types=side_input_types) + pipeline_config.eval_config.use_moving_averages = False + config_util.save_pipeline_config(pipeline_config, output_directory) + + +def profile_inference_graph(graph): + """Profiles the inference graph. + + Prints model parameters and computation FLOPs given an inference graph. + BatchNorms are excluded from the parameter count due to the fact that + BatchNorms are usually folded. BatchNorm, Initializer, Regularizer + and BiasAdd are not considered in FLOP count. + + Args: + graph: the inference graph. + """ + tfprof_vars_option = ( + contrib_tfprof.model_analyzer.TRAINABLE_VARS_PARAMS_STAT_OPTIONS) + tfprof_flops_option = contrib_tfprof.model_analyzer.FLOAT_OPS_OPTIONS + + # Batchnorm is usually folded during inference. + tfprof_vars_option['trim_name_regexes'] = ['.*BatchNorm.*'] + # Initializer and Regularizer are only used in training. + tfprof_flops_option['trim_name_regexes'] = [ + '.*BatchNorm.*', '.*Initializer.*', '.*Regularizer.*', '.*BiasAdd.*' + ] + + contrib_tfprof.model_analyzer.print_model_analysis( + graph, tfprof_options=tfprof_vars_option) + + contrib_tfprof.model_analyzer.print_model_analysis( + graph, tfprof_options=tfprof_flops_option) diff --git a/workspace/virtuallab/object_detection/exporter_lib_tf2_test.py b/workspace/virtuallab/object_detection/exporter_lib_tf2_test.py new file mode 100644 index 0000000..8e85e11 --- /dev/null +++ b/workspace/virtuallab/object_detection/exporter_lib_tf2_test.py @@ -0,0 +1,297 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Test for exporter_lib_v2.py.""" + +from __future__ import division +import io +import os +import unittest +from absl.testing import parameterized +import numpy as np +from PIL import Image +import six + +import tensorflow.compat.v2 as tf + +from object_detection import exporter_lib_v2 +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.core import standard_fields as fields +from object_detection.protos import pipeline_pb2 +from object_detection.utils import dataset_util +from object_detection.utils import tf_version + +if six.PY2: + import mock # pylint: disable=g-importing-member,g-import-not-at-top +else: + from unittest import mock # pylint: disable=g-importing-member,g-import-not-at-top + + +class FakeModel(model.DetectionModel): + + def __init__(self, conv_weight_scalar=1.0): + super(FakeModel, self).__init__(num_classes=2) + self._conv = tf.keras.layers.Conv2D( + filters=1, kernel_size=1, strides=(1, 1), padding='valid', + kernel_initializer=tf.keras.initializers.Constant( + value=conv_weight_scalar)) + + def preprocess(self, inputs): + true_image_shapes = [] # Doesn't matter for the fake model. + return tf.identity(inputs), true_image_shapes + + def predict(self, preprocessed_inputs, true_image_shapes, **side_inputs): + return_dict = {'image': self._conv(preprocessed_inputs)} + if 'side_inp_1' in side_inputs: + return_dict['image'] += side_inputs['side_inp_1'] + return return_dict + + def postprocess(self, prediction_dict, true_image_shapes): + predict_tensor_sum = tf.reduce_sum(prediction_dict['image']) + with tf.control_dependencies(list(prediction_dict.values())): + postprocessed_tensors = { + 'detection_boxes': tf.constant([[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]], tf.float32), + 'detection_scores': predict_tensor_sum + tf.constant( + [[0.7, 0.6], [0.9, 0.0]], tf.float32), + 'detection_classes': tf.constant([[0, 1], + [1, 0]], tf.float32), + 'num_detections': tf.constant([2, 1], tf.float32), + } + return postprocessed_tensors + + def restore_map(self, checkpoint_path, fine_tune_checkpoint_type): + pass + + def restore_from_objects(self, fine_tune_checkpoint_type): + pass + + def loss(self, prediction_dict, true_image_shapes): + pass + + def regularization_losses(self): + pass + + def updates(self): + pass + + +@unittest.skipIf(tf_version.is_tf1(), 'Skipping TF2.X only test.') +class ExportInferenceGraphTest(tf.test.TestCase, parameterized.TestCase): + + def _save_checkpoint_from_mock_model( + self, checkpoint_dir, conv_weight_scalar=6.0): + mock_model = FakeModel(conv_weight_scalar) + fake_image = tf.zeros(shape=[1, 10, 10, 3], dtype=tf.float32) + preprocessed_inputs, true_image_shapes = mock_model.preprocess(fake_image) + predictions = mock_model.predict(preprocessed_inputs, true_image_shapes) + mock_model.postprocess(predictions, true_image_shapes) + + ckpt = tf.train.Checkpoint(model=mock_model) + exported_checkpoint_manager = tf.train.CheckpointManager( + ckpt, checkpoint_dir, max_to_keep=1) + exported_checkpoint_manager.save(checkpoint_number=0) + + @parameterized.parameters( + {'input_type': 'image_tensor'}, + {'input_type': 'encoded_image_string_tensor'}, + {'input_type': 'tf_example'}, + ) + def test_export_yields_correct_directory_structure( + self, input_type='image_tensor'): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + exporter_lib_v2.INPUT_BUILDER_UTIL_MAP['model_build'] = mock_builder + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter_lib_v2.export_inference_graph( + input_type=input_type, + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'saved_model.pb'))) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'variables', 'variables.index'))) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'variables', + 'variables.data-00000-of-00001'))) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'checkpoint', 'ckpt-0.index'))) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'checkpoint', 'ckpt-0.data-00000-of-00001'))) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'pipeline.config'))) + + def get_dummy_input(self, input_type): + """Get dummy input for the given input type.""" + + if input_type == 'image_tensor': + return np.zeros((1, 20, 20, 3), dtype=np.uint8) + if input_type == 'float_image_tensor': + return np.zeros((1, 20, 20, 3), dtype=np.float32) + elif input_type == 'encoded_image_string_tensor': + image = Image.new('RGB', (20, 20)) + byte_io = io.BytesIO() + image.save(byte_io, 'PNG') + return [byte_io.getvalue()] + elif input_type == 'tf_example': + image_tensor = tf.zeros((20, 20, 3), dtype=tf.uint8) + encoded_jpeg = tf.image.encode_jpeg(tf.constant(image_tensor)).numpy() + example = tf.train.Example( + features=tf.train.Features( + feature={ + 'image/encoded': + dataset_util.bytes_feature(encoded_jpeg), + 'image/format': + dataset_util.bytes_feature(six.b('jpeg')), + 'image/source_id': + dataset_util.bytes_feature(six.b('image_id')), + })).SerializeToString() + return [example] + + @parameterized.parameters( + {'input_type': 'image_tensor'}, + {'input_type': 'encoded_image_string_tensor'}, + {'input_type': 'tf_example'}, + {'input_type': 'float_image_tensor'}, + ) + def test_export_saved_model_and_run_inference( + self, input_type='image_tensor'): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + exporter_lib_v2.INPUT_BUILDER_UTIL_MAP['model_build'] = mock_builder + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter_lib_v2.export_inference_graph( + input_type=input_type, + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory) + + saved_model_path = os.path.join(output_directory, 'saved_model') + detect_fn = tf.saved_model.load(saved_model_path) + image = self.get_dummy_input(input_type) + detections = detect_fn(tf.constant(image)) + + detection_fields = fields.DetectionResultFields + self.assertAllClose(detections[detection_fields.detection_boxes], + [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(detections[detection_fields.detection_scores], + [[0.7, 0.6], [0.9, 0.0]]) + self.assertAllClose(detections[detection_fields.detection_classes], + [[1, 2], [2, 1]]) + self.assertAllClose(detections[detection_fields.num_detections], [2, 1]) + + @parameterized.parameters( + {'use_default_serving': True}, + {'use_default_serving': False} + ) + def test_export_saved_model_and_run_inference_with_side_inputs( + self, input_type='image_tensor', use_default_serving=True): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + exporter_lib_v2.INPUT_BUILDER_UTIL_MAP['model_build'] = mock_builder + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter_lib_v2.export_inference_graph( + input_type=input_type, + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory, + use_side_inputs=True, + side_input_shapes='1/2,2', + side_input_names='side_inp_1,side_inp_2', + side_input_types='tf.float32,tf.uint8') + + saved_model_path = os.path.join(output_directory, 'saved_model') + detect_fn = tf.saved_model.load(saved_model_path) + detect_fn_sig = detect_fn.signatures['serving_default'] + image = tf.constant(self.get_dummy_input(input_type)) + side_input_1 = np.ones((1,), dtype=np.float32) + side_input_2 = np.ones((2, 2), dtype=np.uint8) + if use_default_serving: + detections = detect_fn_sig(input_tensor=image, + side_inp_1=tf.constant(side_input_1), + side_inp_2=tf.constant(side_input_2)) + else: + detections = detect_fn(image, + tf.constant(side_input_1), + tf.constant(side_input_2)) + + detection_fields = fields.DetectionResultFields + self.assertAllClose(detections[detection_fields.detection_boxes], + [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(detections[detection_fields.detection_scores], + [[400.7, 400.6], [400.9, 400.0]]) + self.assertAllClose(detections[detection_fields.detection_classes], + [[1, 2], [2, 1]]) + self.assertAllClose(detections[detection_fields.num_detections], [2, 1]) + + def test_export_checkpoint_and_run_inference_with_image(self): + tmp_dir = self.get_temp_dir() + self._save_checkpoint_from_mock_model(tmp_dir, conv_weight_scalar=2.0) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + exporter_lib_v2.INPUT_BUILDER_UTIL_MAP['model_build'] = mock_builder + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter_lib_v2.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_dir=tmp_dir, + output_directory=output_directory) + + mock_model = FakeModel() + ckpt = tf.compat.v2.train.Checkpoint( + model=mock_model) + checkpoint_dir = os.path.join(tmp_dir, 'output', 'checkpoint') + manager = tf.compat.v2.train.CheckpointManager( + ckpt, checkpoint_dir, max_to_keep=7) + ckpt.restore(manager.latest_checkpoint).expect_partial() + + fake_image = tf.ones(shape=[1, 5, 5, 3], dtype=tf.float32) + preprocessed_inputs, true_image_shapes = mock_model.preprocess(fake_image) + predictions = mock_model.predict(preprocessed_inputs, true_image_shapes) + detections = mock_model.postprocess(predictions, true_image_shapes) + + # 150 = conv_weight_scalar * height * width * channels = 2 * 5 * 5 * 3. + self.assertAllClose(detections['detection_scores'], + [[150 + 0.7, 150 + 0.6], [150 + 0.9, 150 + 0.0]]) + + +if __name__ == '__main__': + tf.enable_v2_behavior() + tf.test.main() diff --git a/workspace/virtuallab/object_detection/exporter_lib_v2.py b/workspace/virtuallab/object_detection/exporter_lib_v2.py new file mode 100644 index 0000000..5a7a182 --- /dev/null +++ b/workspace/virtuallab/object_detection/exporter_lib_v2.py @@ -0,0 +1,275 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Functions to export object detection inference graph.""" +import ast +import os + +import tensorflow.compat.v2 as tf +from object_detection.builders import model_builder +from object_detection.core import standard_fields as fields +from object_detection.data_decoders import tf_example_decoder +from object_detection.utils import config_util + + +INPUT_BUILDER_UTIL_MAP = { + 'model_build': model_builder.build, +} + + +def _decode_image(encoded_image_string_tensor): + image_tensor = tf.image.decode_image(encoded_image_string_tensor, + channels=3) + image_tensor.set_shape((None, None, 3)) + return image_tensor + + +def _decode_tf_example(tf_example_string_tensor): + tensor_dict = tf_example_decoder.TfExampleDecoder().decode( + tf_example_string_tensor) + image_tensor = tensor_dict[fields.InputDataFields.image] + return image_tensor + + +def _combine_side_inputs(side_input_shapes='', + side_input_types='', + side_input_names=''): + """Zips the side inputs together. + + Args: + side_input_shapes: forward-slash-separated list of comma-separated lists + describing input shapes. + side_input_types: comma-separated list of the types of the inputs. + side_input_names: comma-separated list of the names of the inputs. + + Returns: + a zipped list of side input tuples. + """ + side_input_shapes = [ + ast.literal_eval('[' + x + ']') for x in side_input_shapes.split('/') + ] + side_input_types = eval('[' + side_input_types + ']') # pylint: disable=eval-used + side_input_names = side_input_names.split(',') + return zip(side_input_shapes, side_input_types, side_input_names) + + +class DetectionInferenceModule(tf.Module): + """Detection Inference Module.""" + + def __init__(self, detection_model, + use_side_inputs=False, + zipped_side_inputs=None): + """Initializes a module for detection. + + Args: + detection_model: the detection model to use for inference. + use_side_inputs: whether to use side inputs. + zipped_side_inputs: the zipped side inputs. + """ + self._model = detection_model + + def _get_side_input_signature(self, zipped_side_inputs): + sig = [] + side_input_names = [] + for info in zipped_side_inputs: + sig.append(tf.TensorSpec(shape=info[0], + dtype=info[1], + name=info[2])) + side_input_names.append(info[2]) + return sig + + def _get_side_names_from_zip(self, zipped_side_inputs): + return [side[2] for side in zipped_side_inputs] + + def _run_inference_on_images(self, image, **kwargs): + """Cast image to float and run inference. + + Args: + image: uint8 Tensor of shape [1, None, None, 3]. + **kwargs: additional keyword arguments. + + Returns: + Tensor dictionary holding detections. + """ + label_id_offset = 1 + + image = tf.cast(image, tf.float32) + image, shapes = self._model.preprocess(image) + prediction_dict = self._model.predict(image, shapes, **kwargs) + detections = self._model.postprocess(prediction_dict, shapes) + classes_field = fields.DetectionResultFields.detection_classes + detections[classes_field] = ( + tf.cast(detections[classes_field], tf.float32) + label_id_offset) + + for key, val in detections.items(): + detections[key] = tf.cast(val, tf.float32) + + return detections + + +class DetectionFromImageModule(DetectionInferenceModule): + """Detection Inference Module for image inputs.""" + + def __init__(self, detection_model, + use_side_inputs=False, + zipped_side_inputs=None): + """Initializes a module for detection. + + Args: + detection_model: the detection model to use for inference. + use_side_inputs: whether to use side inputs. + zipped_side_inputs: the zipped side inputs. + """ + if zipped_side_inputs is None: + zipped_side_inputs = [] + sig = [tf.TensorSpec(shape=[1, None, None, 3], + dtype=tf.uint8, + name='input_tensor')] + if use_side_inputs: + sig.extend(self._get_side_input_signature(zipped_side_inputs)) + self._side_input_names = self._get_side_names_from_zip(zipped_side_inputs) + + def call_func(input_tensor, *side_inputs): + kwargs = dict(zip(self._side_input_names, side_inputs)) + return self._run_inference_on_images(input_tensor, **kwargs) + + self.__call__ = tf.function(call_func, input_signature=sig) + + # TODO(kaushikshiv): Check if omitting the signature also works. + super(DetectionFromImageModule, self).__init__(detection_model, + use_side_inputs, + zipped_side_inputs) + + +class DetectionFromFloatImageModule(DetectionInferenceModule): + """Detection Inference Module for float image inputs.""" + + @tf.function( + input_signature=[ + tf.TensorSpec(shape=[1, None, None, 3], dtype=tf.float32)]) + def __call__(self, input_tensor): + return self._run_inference_on_images(input_tensor) + + +class DetectionFromEncodedImageModule(DetectionInferenceModule): + """Detection Inference Module for encoded image string inputs.""" + + @tf.function(input_signature=[tf.TensorSpec(shape=[1], dtype=tf.string)]) + def __call__(self, input_tensor): + with tf.device('cpu:0'): + image = tf.map_fn( + _decode_image, + elems=input_tensor, + dtype=tf.uint8, + parallel_iterations=32, + back_prop=False) + return self._run_inference_on_images(image) + + +class DetectionFromTFExampleModule(DetectionInferenceModule): + """Detection Inference Module for TF.Example inputs.""" + + @tf.function(input_signature=[tf.TensorSpec(shape=[1], dtype=tf.string)]) + def __call__(self, input_tensor): + with tf.device('cpu:0'): + image = tf.map_fn( + _decode_tf_example, + elems=input_tensor, + dtype=tf.uint8, + parallel_iterations=32, + back_prop=False) + return self._run_inference_on_images(image) + +DETECTION_MODULE_MAP = { + 'image_tensor': DetectionFromImageModule, + 'encoded_image_string_tensor': + DetectionFromEncodedImageModule, + 'tf_example': DetectionFromTFExampleModule, + 'float_image_tensor': DetectionFromFloatImageModule +} + + +def export_inference_graph(input_type, + pipeline_config, + trained_checkpoint_dir, + output_directory, + use_side_inputs=False, + side_input_shapes='', + side_input_types='', + side_input_names=''): + """Exports inference graph for the model specified in the pipeline config. + + This function creates `output_directory` if it does not already exist, + which will hold a copy of the pipeline config with filename `pipeline.config`, + and two subdirectories named `checkpoint` and `saved_model` + (containing the exported checkpoint and SavedModel respectively). + + Args: + input_type: Type of input for the graph. Can be one of ['image_tensor', + 'encoded_image_string_tensor', 'tf_example']. + pipeline_config: pipeline_pb2.TrainAndEvalPipelineConfig proto. + trained_checkpoint_dir: Path to the trained checkpoint file. + output_directory: Path to write outputs. + use_side_inputs: boolean that determines whether side inputs should be + included in the input signature. + side_input_shapes: forward-slash-separated list of comma-separated lists + describing input shapes. + side_input_types: comma-separated list of the types of the inputs. + side_input_names: comma-separated list of the names of the inputs. + Raises: + ValueError: if input_type is invalid. + """ + output_checkpoint_directory = os.path.join(output_directory, 'checkpoint') + output_saved_model_directory = os.path.join(output_directory, 'saved_model') + + detection_model = INPUT_BUILDER_UTIL_MAP['model_build']( + pipeline_config.model, is_training=False) + + ckpt = tf.train.Checkpoint( + model=detection_model) + manager = tf.train.CheckpointManager( + ckpt, trained_checkpoint_dir, max_to_keep=1) + status = ckpt.restore(manager.latest_checkpoint).expect_partial() + + if input_type not in DETECTION_MODULE_MAP: + raise ValueError('Unrecognized `input_type`') + if use_side_inputs and input_type != 'image_tensor': + raise ValueError('Side inputs supported for image_tensor input type only.') + + zipped_side_inputs = [] + if use_side_inputs: + zipped_side_inputs = _combine_side_inputs(side_input_shapes, + side_input_types, + side_input_names) + + detection_module = DETECTION_MODULE_MAP[input_type](detection_model, + use_side_inputs, + list(zipped_side_inputs)) + # Getting the concrete function traces the graph and forces variables to + # be constructed --- only after this can we save the checkpoint and + # saved model. + concrete_function = detection_module.__call__.get_concrete_function() + status.assert_existing_objects_matched() + + exported_checkpoint_manager = tf.train.CheckpointManager( + ckpt, output_checkpoint_directory, max_to_keep=1) + exported_checkpoint_manager.save(checkpoint_number=0) + + tf.saved_model.save(detection_module, + output_saved_model_directory, + signatures=concrete_function) + + config_util.save_pipeline_config(pipeline_config, output_directory) diff --git a/workspace/virtuallab/object_detection/exporter_main_v2.py b/workspace/virtuallab/object_detection/exporter_main_v2.py new file mode 100644 index 0000000..bb4a01a --- /dev/null +++ b/workspace/virtuallab/object_detection/exporter_main_v2.py @@ -0,0 +1,159 @@ +# Lint as: python2, python3 +# Copyright 2020 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +r"""Tool to export an object detection model for inference. + +Prepares an object detection tensorflow graph for inference using model +configuration and a trained checkpoint. Outputs associated checkpoint files, +a SavedModel, and a copy of the model config. + +The inference graph contains one of three input nodes depending on the user +specified option. + * `image_tensor`: Accepts a uint8 4-D tensor of shape [1, None, None, 3] + * `float_image_tensor`: Accepts a float32 4-D tensor of shape + [1, None, None, 3] + * `encoded_image_string_tensor`: Accepts a 1-D string tensor of shape [None] + containing encoded PNG or JPEG images. Image resolutions are expected to be + the same if more than 1 image is provided. + * `tf_example`: Accepts a 1-D string tensor of shape [None] containing + serialized TFExample protos. Image resolutions are expected to be the same + if more than 1 image is provided. + +and the following output nodes returned by the model.postprocess(..): + * `num_detections`: Outputs float32 tensors of the form [batch] + that specifies the number of valid boxes per image in the batch. + * `detection_boxes`: Outputs float32 tensors of the form + [batch, num_boxes, 4] containing detected boxes. + * `detection_scores`: Outputs float32 tensors of the form + [batch, num_boxes] containing class scores for the detections. + * `detection_classes`: Outputs float32 tensors of the form + [batch, num_boxes] containing classes for the detections. + + +Example Usage: +-------------- +python exporter_main_v2.py \ + --input_type image_tensor \ + --pipeline_config_path path/to/ssd_inception_v2.config \ + --trained_checkpoint_dir path/to/checkpoint \ + --output_directory path/to/exported_model_directory + --use_side_inputs True/False \ + --side_input_shapes dim_0,dim_1,...dim_a/.../dim_0,dim_1,...,dim_z \ + --side_input_names name_a,name_b,...,name_c \ + --side_input_types type_1,type_2 + +The expected output would be in the directory +path/to/exported_model_directory (which is created if it does not exist) +holding two subdirectories (corresponding to checkpoint and SavedModel, +respectively) and a copy of the pipeline config. + +Config overrides (see the `config_override` flag) are text protobufs +(also of type pipeline_pb2.TrainEvalPipelineConfig) which are used to override +certain fields in the provided pipeline_config_path. These are useful for +making small changes to the inference graph that differ from the training or +eval config. + +Example Usage (in which we change the second stage post-processing score +threshold to be 0.5): + +python exporter_main_v2.py \ + --input_type image_tensor \ + --pipeline_config_path path/to/ssd_inception_v2.config \ + --trained_checkpoint_dir path/to/checkpoint \ + --output_directory path/to/exported_model_directory \ + --config_override " \ + model{ \ + faster_rcnn { \ + second_stage_post_processing { \ + batch_non_max_suppression { \ + score_threshold: 0.5 \ + } \ + } \ + } \ + }" + +If side inputs are desired, the following arguments could be appended +(the example below is for Context R-CNN). + --use_side_inputs True \ + --side_input_shapes 1,2000,2057/1 \ + --side_input_names context_features,valid_context_size \ + --side_input_types tf.float32,tf.int32 +""" +from absl import app +from absl import flags + +import tensorflow.compat.v2 as tf +from google.protobuf import text_format +from object_detection import exporter_lib_v2 +from object_detection.protos import pipeline_pb2 + +tf.enable_v2_behavior() + + +FLAGS = flags.FLAGS + +flags.DEFINE_string('input_type', 'image_tensor', 'Type of input node. Can be ' + 'one of [`image_tensor`, `encoded_image_string_tensor`, ' + '`tf_example`, `float_image_tensor`]') +flags.DEFINE_string('pipeline_config_path', None, + 'Path to a pipeline_pb2.TrainEvalPipelineConfig config ' + 'file.') +flags.DEFINE_string('trained_checkpoint_dir', None, + 'Path to trained checkpoint directory') +flags.DEFINE_string('output_directory', None, 'Path to write outputs.') +flags.DEFINE_string('config_override', '', + 'pipeline_pb2.TrainEvalPipelineConfig ' + 'text proto to override pipeline_config_path.') +flags.DEFINE_boolean('use_side_inputs', False, + 'If True, uses side inputs as well as image inputs.') +flags.DEFINE_string('side_input_shapes', '', + 'If use_side_inputs is True, this explicitly sets ' + 'the shape of the side input tensors to a fixed size. The ' + 'dimensions are to be provided as a comma-separated list ' + 'of integers. A value of -1 can be used for unknown ' + 'dimensions. A `/` denotes a break, starting the shape of ' + 'the next side input tensor. This flag is required if ' + 'using side inputs.') +flags.DEFINE_string('side_input_types', '', + 'If use_side_inputs is True, this explicitly sets ' + 'the type of the side input tensors. The ' + 'dimensions are to be provided as a comma-separated list ' + 'of types, each of `string`, `integer`, or `float`. ' + 'This flag is required if using side inputs.') +flags.DEFINE_string('side_input_names', '', + 'If use_side_inputs is True, this explicitly sets ' + 'the names of the side input tensors required by the model ' + 'assuming the names will be a comma-separated list of ' + 'strings. This flag is required if using side inputs.') + +flags.mark_flag_as_required('pipeline_config_path') +flags.mark_flag_as_required('trained_checkpoint_dir') +flags.mark_flag_as_required('output_directory') + + +def main(_): + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + with tf.io.gfile.GFile(FLAGS.pipeline_config_path, 'r') as f: + text_format.Merge(f.read(), pipeline_config) + text_format.Merge(FLAGS.config_override, pipeline_config) + exporter_lib_v2.export_inference_graph( + FLAGS.input_type, pipeline_config, FLAGS.trained_checkpoint_dir, + FLAGS.output_directory, FLAGS.use_side_inputs, FLAGS.side_input_shapes, + FLAGS.side_input_types, FLAGS.side_input_names) + + +if __name__ == '__main__': + app.run(main) diff --git a/workspace/virtuallab/object_detection/exporter_tf1_test.py b/workspace/virtuallab/object_detection/exporter_tf1_test.py new file mode 100644 index 0000000..b33bafd --- /dev/null +++ b/workspace/virtuallab/object_detection/exporter_tf1_test.py @@ -0,0 +1,1206 @@ +# Lint as: python2, python3 +# Copyright 2017 The TensorFlow Authors. All Rights Reserved. +# +# 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. +# ============================================================================== + +"""Tests for object_detection.export_inference_graph.""" +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +import os +import unittest +import numpy as np +import six +import tensorflow.compat.v1 as tf +from google.protobuf import text_format +from tensorflow.python.framework import dtypes +from tensorflow.python.ops import array_ops +from tensorflow.python.tools import strip_unused_lib +from object_detection import exporter +from object_detection.builders import graph_rewriter_builder +from object_detection.builders import model_builder +from object_detection.core import model +from object_detection.protos import graph_rewriter_pb2 +from object_detection.protos import pipeline_pb2 +from object_detection.utils import ops +from object_detection.utils import tf_version +from object_detection.utils import variables_helper + +if six.PY2: + import mock # pylint: disable=g-import-not-at-top +else: + mock = unittest.mock # pylint: disable=g-import-not-at-top, g-importing-member + +# pylint: disable=g-import-not-at-top +try: + import tf_slim as slim +except ImportError: + # TF 2.0 doesn't ship with contrib. + pass +# pylint: enable=g-import-not-at-top + + +class FakeModel(model.DetectionModel): + + def __init__(self, add_detection_keypoints=False, add_detection_masks=False, + add_detection_features=False): + self._add_detection_keypoints = add_detection_keypoints + self._add_detection_masks = add_detection_masks + self._add_detection_features = add_detection_features + + def preprocess(self, inputs): + true_image_shapes = [] # Doesn't matter for the fake model. + return tf.identity(inputs), true_image_shapes + + def predict(self, preprocessed_inputs, true_image_shapes): + return {'image': tf.layers.conv2d(preprocessed_inputs, 3, 1)} + + def postprocess(self, prediction_dict, true_image_shapes): + with tf.control_dependencies(list(prediction_dict.values())): + postprocessed_tensors = { + 'detection_boxes': tf.constant([[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]], tf.float32), + 'detection_scores': tf.constant([[0.7, 0.6], + [0.9, 0.0]], tf.float32), + 'detection_multiclass_scores': tf.constant([[[0.3, 0.7], [0.4, 0.6]], + [[0.1, 0.9], [0.0, 0.0]]], + tf.float32), + 'detection_classes': tf.constant([[0, 1], + [1, 0]], tf.float32), + 'num_detections': tf.constant([2, 1], tf.float32), + 'raw_detection_boxes': tf.constant([[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.5, 0.0, 0.5]]], + tf.float32), + 'raw_detection_scores': tf.constant([[0.7, 0.6], + [0.9, 0.5]], tf.float32), + } + if self._add_detection_keypoints: + postprocessed_tensors['detection_keypoints'] = tf.constant( + np.arange(48).reshape([2, 2, 6, 2]), tf.float32) + if self._add_detection_masks: + postprocessed_tensors['detection_masks'] = tf.constant( + np.arange(64).reshape([2, 2, 4, 4]), tf.float32) + if self._add_detection_features: + # let fake detection features have shape [4, 4, 10] + postprocessed_tensors['detection_features'] = tf.constant( + np.ones((2, 2, 4, 4, 10)), tf.float32) + + return postprocessed_tensors + + def restore_map(self, checkpoint_path, fine_tune_checkpoint_type): + pass + + def restore_from_objects(self, fine_tune_checkpoint_type): + pass + + def loss(self, prediction_dict, true_image_shapes): + pass + + def regularization_losses(self): + pass + + def updates(self): + pass + + +@unittest.skipIf(tf_version.is_tf2(), 'Skipping TF1.X only test.') +class ExportInferenceGraphTest(tf.test.TestCase): + + def _save_checkpoint_from_mock_model(self, + checkpoint_path, + use_moving_averages, + enable_quantization=False): + g = tf.Graph() + with g.as_default(): + mock_model = FakeModel() + preprocessed_inputs, true_image_shapes = mock_model.preprocess( + tf.placeholder(tf.float32, shape=[None, None, None, 3])) + predictions = mock_model.predict(preprocessed_inputs, true_image_shapes) + mock_model.postprocess(predictions, true_image_shapes) + if use_moving_averages: + tf.train.ExponentialMovingAverage(0.0).apply() + tf.train.get_or_create_global_step() + if enable_quantization: + graph_rewriter_config = graph_rewriter_pb2.GraphRewriter() + graph_rewriter_config.quantization.delay = 500000 + graph_rewriter_fn = graph_rewriter_builder.build( + graph_rewriter_config, is_training=False) + graph_rewriter_fn() + saver = tf.train.Saver() + init = tf.global_variables_initializer() + with self.test_session() as sess: + sess.run(init) + saver.save(sess, checkpoint_path) + + def _load_inference_graph(self, inference_graph_path, is_binary=True): + od_graph = tf.Graph() + with od_graph.as_default(): + od_graph_def = tf.GraphDef() + with tf.gfile.GFile(inference_graph_path, mode='rb') as fid: + if is_binary: + od_graph_def.ParseFromString(fid.read()) + else: + text_format.Parse(fid.read(), od_graph_def) + tf.import_graph_def(od_graph_def, name='') + return od_graph + + def _create_tf_example(self, image_array): + with self.test_session(): + encoded_image = tf.image.encode_jpeg(tf.constant(image_array)).eval() + def _bytes_feature(value): + return tf.train.Feature( + bytes_list=tf.train.BytesList(value=[six.ensure_binary(value)])) + + example = tf.train.Example(features=tf.train.Features(feature={ + 'image/encoded': _bytes_feature(encoded_image), + 'image/format': _bytes_feature('jpg'), + 'image/source_id': _bytes_feature('image_id') + })).SerializeToString() + return example + + def test_export_graph_with_image_tensor_input(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'saved_model.pb'))) + + def test_write_inference_graph(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory, + write_inference_graph=True) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'inference_graph.pbtxt'))) + + def test_export_graph_with_fixed_size_image_tensor_input(self): + input_shape = [1, 320, 320, 3] + + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model( + trained_checkpoint_prefix, use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory, + input_shape=input_shape) + saved_model_path = os.path.join(output_directory, 'saved_model') + self.assertTrue( + os.path.exists(os.path.join(saved_model_path, 'saved_model.pb'))) + + with tf.Graph().as_default() as od_graph: + with self.test_session(graph=od_graph) as sess: + meta_graph = tf.saved_model.loader.load( + sess, [tf.saved_model.tag_constants.SERVING], saved_model_path) + signature = meta_graph.signature_def['serving_default'] + input_tensor_name = signature.inputs['inputs'].name + image_tensor = od_graph.get_tensor_by_name(input_tensor_name) + self.assertSequenceEqual(image_tensor.get_shape().as_list(), + input_shape) + + def test_export_graph_with_tf_example_input(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'saved_model.pb'))) + + def test_export_graph_with_fixed_size_tf_example_input(self): + input_shape = [1, 320, 320, 3] + + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model( + trained_checkpoint_prefix, use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory, + input_shape=input_shape) + saved_model_path = os.path.join(output_directory, 'saved_model') + self.assertTrue( + os.path.exists(os.path.join(saved_model_path, 'saved_model.pb'))) + + def test_export_graph_with_encoded_image_string_input(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='encoded_image_string_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'saved_model.pb'))) + + def test_export_graph_with_fixed_size_encoded_image_string_input(self): + input_shape = [1, 320, 320, 3] + + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model( + trained_checkpoint_prefix, use_moving_averages=False) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + output_directory = os.path.join(tmp_dir, 'output') + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='encoded_image_string_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory, + input_shape=input_shape) + saved_model_path = os.path.join(output_directory, 'saved_model') + self.assertTrue( + os.path.exists(os.path.join(saved_model_path, 'saved_model.pb'))) + + def _get_variables_in_checkpoint(self, checkpoint_file): + return set([ + var_name + for var_name, _ in tf.train.list_variables(checkpoint_file)]) + + def test_replace_variable_values_with_moving_averages(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + new_checkpoint_prefix = os.path.join(tmp_dir, 'new.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + graph = tf.Graph() + with graph.as_default(): + fake_model = FakeModel() + preprocessed_inputs, true_image_shapes = fake_model.preprocess( + tf.placeholder(dtype=tf.float32, shape=[None, None, None, 3])) + predictions = fake_model.predict(preprocessed_inputs, true_image_shapes) + fake_model.postprocess(predictions, true_image_shapes) + exporter.replace_variable_values_with_moving_averages( + graph, trained_checkpoint_prefix, new_checkpoint_prefix) + + expected_variables = set(['conv2d/bias', 'conv2d/kernel']) + variables_in_old_ckpt = self._get_variables_in_checkpoint( + trained_checkpoint_prefix) + self.assertIn('conv2d/bias/ExponentialMovingAverage', + variables_in_old_ckpt) + self.assertIn('conv2d/kernel/ExponentialMovingAverage', + variables_in_old_ckpt) + variables_in_new_ckpt = self._get_variables_in_checkpoint( + new_checkpoint_prefix) + self.assertTrue(expected_variables.issubset(variables_in_new_ckpt)) + self.assertNotIn('conv2d/bias/ExponentialMovingAverage', + variables_in_new_ckpt) + self.assertNotIn('conv2d/kernel/ExponentialMovingAverage', + variables_in_new_ckpt) + + def test_export_graph_with_moving_averages(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = True + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + self.assertTrue(os.path.exists(os.path.join( + output_directory, 'saved_model', 'saved_model.pb'))) + expected_variables = set(['conv2d/bias', 'conv2d/kernel', 'global_step']) + actual_variables = set( + [var_name for var_name, _ in tf.train.list_variables(output_directory)]) + self.assertTrue(expected_variables.issubset(actual_variables)) + + def test_export_model_with_quantization_nodes(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model( + trained_checkpoint_prefix, + use_moving_averages=False, + enable_quantization=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'inference_graph.pbtxt') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + text_format.Merge( + """graph_rewriter { + quantization { + delay: 50000 + activation_bits: 8 + weight_bits: 8 + } + }""", pipeline_config) + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory, + write_inference_graph=True) + self._load_inference_graph(inference_graph_path, is_binary=False) + has_quant_nodes = False + for v in variables_helper.get_global_variables_safely(): + if six.ensure_str(v.op.name).endswith('act_quant/min'): + has_quant_nodes = True + break + self.assertTrue(has_quant_nodes) + + def test_export_model_with_all_output_nodes(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True, + add_detection_features=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + inference_graph = self._load_inference_graph(inference_graph_path) + with self.test_session(graph=inference_graph): + inference_graph.get_tensor_by_name('image_tensor:0') + inference_graph.get_tensor_by_name('detection_boxes:0') + inference_graph.get_tensor_by_name('detection_scores:0') + inference_graph.get_tensor_by_name('detection_multiclass_scores:0') + inference_graph.get_tensor_by_name('detection_classes:0') + inference_graph.get_tensor_by_name('detection_keypoints:0') + inference_graph.get_tensor_by_name('detection_masks:0') + inference_graph.get_tensor_by_name('num_detections:0') + inference_graph.get_tensor_by_name('detection_features:0') + + def test_export_model_with_detection_only_nodes(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(add_detection_masks=False) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + inference_graph = self._load_inference_graph(inference_graph_path) + with self.test_session(graph=inference_graph): + inference_graph.get_tensor_by_name('image_tensor:0') + inference_graph.get_tensor_by_name('detection_boxes:0') + inference_graph.get_tensor_by_name('detection_scores:0') + inference_graph.get_tensor_by_name('detection_multiclass_scores:0') + inference_graph.get_tensor_by_name('detection_classes:0') + inference_graph.get_tensor_by_name('num_detections:0') + with self.assertRaises(KeyError): + inference_graph.get_tensor_by_name('detection_keypoints:0') + inference_graph.get_tensor_by_name('detection_masks:0') + + def test_export_model_with_detection_only_nodes_and_detection_features(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel(add_detection_features=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + inference_graph = self._load_inference_graph(inference_graph_path) + with self.test_session(graph=inference_graph): + inference_graph.get_tensor_by_name('image_tensor:0') + inference_graph.get_tensor_by_name('detection_boxes:0') + inference_graph.get_tensor_by_name('detection_scores:0') + inference_graph.get_tensor_by_name('detection_multiclass_scores:0') + inference_graph.get_tensor_by_name('detection_classes:0') + inference_graph.get_tensor_by_name('num_detections:0') + inference_graph.get_tensor_by_name('detection_features:0') + with self.assertRaises(KeyError): + inference_graph.get_tensor_by_name('detection_keypoints:0') + inference_graph.get_tensor_by_name('detection_masks:0') + + def test_export_and_run_inference_with_image_tensor(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + + inference_graph = self._load_inference_graph(inference_graph_path) + with self.test_session(graph=inference_graph) as sess: + image_tensor = inference_graph.get_tensor_by_name('image_tensor:0') + boxes = inference_graph.get_tensor_by_name('detection_boxes:0') + scores = inference_graph.get_tensor_by_name('detection_scores:0') + classes = inference_graph.get_tensor_by_name('detection_classes:0') + keypoints = inference_graph.get_tensor_by_name('detection_keypoints:0') + masks = inference_graph.get_tensor_by_name('detection_masks:0') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + (boxes_np, scores_np, classes_np, keypoints_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, keypoints, masks, num_detections], + feed_dict={image_tensor: np.ones((2, 4, 4, 3)).astype(np.uint8)}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def _create_encoded_image_string(self, image_array_np, encoding_format): + od_graph = tf.Graph() + with od_graph.as_default(): + if encoding_format == 'jpg': + encoded_string = tf.image.encode_jpeg(image_array_np) + elif encoding_format == 'png': + encoded_string = tf.image.encode_png(image_array_np) + else: + raise ValueError('Supports only the following formats: `jpg`, `png`') + with self.test_session(graph=od_graph): + return encoded_string.eval() + + def test_export_and_run_inference_with_encoded_image_string_tensor(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='encoded_image_string_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + + inference_graph = self._load_inference_graph(inference_graph_path) + jpg_image_str = self._create_encoded_image_string( + np.ones((4, 4, 3)).astype(np.uint8), 'jpg') + png_image_str = self._create_encoded_image_string( + np.ones((4, 4, 3)).astype(np.uint8), 'png') + with self.test_session(graph=inference_graph) as sess: + image_str_tensor = inference_graph.get_tensor_by_name( + 'encoded_image_string_tensor:0') + boxes = inference_graph.get_tensor_by_name('detection_boxes:0') + scores = inference_graph.get_tensor_by_name('detection_scores:0') + multiclass_scores = inference_graph.get_tensor_by_name( + 'detection_multiclass_scores:0') + classes = inference_graph.get_tensor_by_name('detection_classes:0') + keypoints = inference_graph.get_tensor_by_name('detection_keypoints:0') + masks = inference_graph.get_tensor_by_name('detection_masks:0') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + for image_str in [jpg_image_str, png_image_str]: + image_str_batch_np = np.hstack([image_str]* 2) + (boxes_np, scores_np, multiclass_scores_np, classes_np, keypoints_np, + masks_np, num_detections_np) = sess.run( + [ + boxes, scores, multiclass_scores, classes, keypoints, masks, + num_detections + ], + feed_dict={image_str_tensor: image_str_batch_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(multiclass_scores_np, [[[0.3, 0.7], [0.4, 0.6]], + [[0.1, 0.9], [0.0, 0.0]]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_raise_runtime_error_on_images_with_different_sizes(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='encoded_image_string_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + + inference_graph = self._load_inference_graph(inference_graph_path) + large_image = self._create_encoded_image_string( + np.ones((4, 4, 3)).astype(np.uint8), 'jpg') + small_image = self._create_encoded_image_string( + np.ones((2, 2, 3)).astype(np.uint8), 'jpg') + + image_str_batch_np = np.hstack([large_image, small_image]) + with self.test_session(graph=inference_graph) as sess: + image_str_tensor = inference_graph.get_tensor_by_name( + 'encoded_image_string_tensor:0') + boxes = inference_graph.get_tensor_by_name('detection_boxes:0') + scores = inference_graph.get_tensor_by_name('detection_scores:0') + classes = inference_graph.get_tensor_by_name('detection_classes:0') + keypoints = inference_graph.get_tensor_by_name('detection_keypoints:0') + masks = inference_graph.get_tensor_by_name('detection_masks:0') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + with self.assertRaisesRegexp(tf.errors.InvalidArgumentError, + 'TensorArray.*shape'): + sess.run( + [boxes, scores, classes, keypoints, masks, num_detections], + feed_dict={image_str_tensor: image_str_batch_np}) + + def test_export_and_run_inference_with_tf_example(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + + inference_graph = self._load_inference_graph(inference_graph_path) + tf_example_np = np.expand_dims(self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8)), axis=0) + with self.test_session(graph=inference_graph) as sess: + tf_example = inference_graph.get_tensor_by_name('tf_example:0') + boxes = inference_graph.get_tensor_by_name('detection_boxes:0') + scores = inference_graph.get_tensor_by_name('detection_scores:0') + classes = inference_graph.get_tensor_by_name('detection_classes:0') + keypoints = inference_graph.get_tensor_by_name('detection_keypoints:0') + masks = inference_graph.get_tensor_by_name('detection_masks:0') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + (boxes_np, scores_np, classes_np, keypoints_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, keypoints, masks, num_detections], + feed_dict={tf_example: tf_example_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_write_frozen_graph(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + inference_graph_path = os.path.join(output_directory, + 'frozen_inference_graph.pb') + tf.gfile.MakeDirs(output_directory) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + detection_model = model_builder.build(pipeline_config.model, + is_training=False) + outputs, _ = exporter.build_detection_graph( + input_type='tf_example', + detection_model=detection_model, + input_shape=None, + output_collection_name='inference_op', + graph_hook_fn=None) + output_node_names = ','.join(list(outputs.keys())) + saver = tf.train.Saver() + input_saver_def = saver.as_saver_def() + exporter.freeze_graph_with_def_protos( + input_graph_def=tf.get_default_graph().as_graph_def(), + input_saver_def=input_saver_def, + input_checkpoint=trained_checkpoint_prefix, + output_node_names=output_node_names, + restore_op_name='save/restore_all', + filename_tensor_name='save/Const:0', + output_graph=inference_graph_path, + clear_devices=True, + initializer_nodes='') + + inference_graph = self._load_inference_graph(inference_graph_path) + tf_example_np = np.expand_dims(self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8)), axis=0) + with self.test_session(graph=inference_graph) as sess: + tf_example = inference_graph.get_tensor_by_name('tf_example:0') + boxes = inference_graph.get_tensor_by_name('detection_boxes:0') + scores = inference_graph.get_tensor_by_name('detection_scores:0') + classes = inference_graph.get_tensor_by_name('detection_classes:0') + keypoints = inference_graph.get_tensor_by_name('detection_keypoints:0') + masks = inference_graph.get_tensor_by_name('detection_masks:0') + num_detections = inference_graph.get_tensor_by_name('num_detections:0') + (boxes_np, scores_np, classes_np, keypoints_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, keypoints, masks, num_detections], + feed_dict={tf_example: tf_example_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_export_graph_saves_pipeline_file(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=True) + output_directory = os.path.join(tmp_dir, 'output') + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel() + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + exporter.export_inference_graph( + input_type='image_tensor', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + expected_pipeline_path = os.path.join( + output_directory, 'pipeline.config') + self.assertTrue(os.path.exists(expected_pipeline_path)) + + written_pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + with tf.gfile.GFile(expected_pipeline_path, 'r') as f: + proto_str = f.read() + text_format.Merge(proto_str, written_pipeline_config) + self.assertProtoEquals(pipeline_config, written_pipeline_config) + + def test_export_saved_model_and_run_inference(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + output_directory = os.path.join(tmp_dir, 'output') + saved_model_path = os.path.join(output_directory, 'saved_model') + + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + + tf_example_np = np.hstack([self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8))] * 2) + with tf.Graph().as_default() as od_graph: + with self.test_session(graph=od_graph) as sess: + meta_graph = tf.saved_model.loader.load( + sess, [tf.saved_model.tag_constants.SERVING], saved_model_path) + + signature = meta_graph.signature_def['serving_default'] + input_tensor_name = signature.inputs['inputs'].name + tf_example = od_graph.get_tensor_by_name(input_tensor_name) + + boxes = od_graph.get_tensor_by_name( + signature.outputs['detection_boxes'].name) + scores = od_graph.get_tensor_by_name( + signature.outputs['detection_scores'].name) + multiclass_scores = od_graph.get_tensor_by_name( + signature.outputs['detection_multiclass_scores'].name) + classes = od_graph.get_tensor_by_name( + signature.outputs['detection_classes'].name) + keypoints = od_graph.get_tensor_by_name( + signature.outputs['detection_keypoints'].name) + masks = od_graph.get_tensor_by_name( + signature.outputs['detection_masks'].name) + num_detections = od_graph.get_tensor_by_name( + signature.outputs['num_detections'].name) + + (boxes_np, scores_np, multiclass_scores_np, classes_np, keypoints_np, + masks_np, num_detections_np) = sess.run( + [boxes, scores, multiclass_scores, classes, keypoints, masks, + num_detections], + feed_dict={tf_example: tf_example_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(multiclass_scores_np, [[[0.3, 0.7], [0.4, 0.6]], + [[0.1, 0.9], [0.0, 0.0]]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_write_saved_model(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + output_directory = os.path.join(tmp_dir, 'output') + saved_model_path = os.path.join(output_directory, 'saved_model') + tf.gfile.MakeDirs(output_directory) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + detection_model = model_builder.build(pipeline_config.model, + is_training=False) + outputs, placeholder_tensor = exporter.build_detection_graph( + input_type='tf_example', + detection_model=detection_model, + input_shape=None, + output_collection_name='inference_op', + graph_hook_fn=None) + output_node_names = ','.join(list(outputs.keys())) + saver = tf.train.Saver() + input_saver_def = saver.as_saver_def() + frozen_graph_def = exporter.freeze_graph_with_def_protos( + input_graph_def=tf.get_default_graph().as_graph_def(), + input_saver_def=input_saver_def, + input_checkpoint=trained_checkpoint_prefix, + output_node_names=output_node_names, + restore_op_name='save/restore_all', + filename_tensor_name='save/Const:0', + output_graph='', + clear_devices=True, + initializer_nodes='') + exporter.write_saved_model( + saved_model_path=saved_model_path, + frozen_graph_def=frozen_graph_def, + inputs=placeholder_tensor, + outputs=outputs) + + tf_example_np = np.hstack([self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8))] * 2) + with tf.Graph().as_default() as od_graph: + with self.test_session(graph=od_graph) as sess: + meta_graph = tf.saved_model.loader.load( + sess, [tf.saved_model.tag_constants.SERVING], saved_model_path) + + signature = meta_graph.signature_def['serving_default'] + input_tensor_name = signature.inputs['inputs'].name + tf_example = od_graph.get_tensor_by_name(input_tensor_name) + + boxes = od_graph.get_tensor_by_name( + signature.outputs['detection_boxes'].name) + scores = od_graph.get_tensor_by_name( + signature.outputs['detection_scores'].name) + classes = od_graph.get_tensor_by_name( + signature.outputs['detection_classes'].name) + keypoints = od_graph.get_tensor_by_name( + signature.outputs['detection_keypoints'].name) + masks = od_graph.get_tensor_by_name( + signature.outputs['detection_masks'].name) + num_detections = od_graph.get_tensor_by_name( + signature.outputs['num_detections'].name) + + (boxes_np, scores_np, classes_np, keypoints_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, keypoints, masks, num_detections], + feed_dict={tf_example: tf_example_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_export_checkpoint_and_run_inference(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + output_directory = os.path.join(tmp_dir, 'output') + model_path = os.path.join(output_directory, 'model.ckpt') + meta_graph_path = model_path + '.meta' + + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + exporter.export_inference_graph( + input_type='tf_example', + pipeline_config=pipeline_config, + trained_checkpoint_prefix=trained_checkpoint_prefix, + output_directory=output_directory) + + tf_example_np = np.hstack([self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8))] * 2) + with tf.Graph().as_default() as od_graph: + with self.test_session(graph=od_graph) as sess: + new_saver = tf.train.import_meta_graph(meta_graph_path) + new_saver.restore(sess, model_path) + + tf_example = od_graph.get_tensor_by_name('tf_example:0') + boxes = od_graph.get_tensor_by_name('detection_boxes:0') + scores = od_graph.get_tensor_by_name('detection_scores:0') + classes = od_graph.get_tensor_by_name('detection_classes:0') + keypoints = od_graph.get_tensor_by_name('detection_keypoints:0') + masks = od_graph.get_tensor_by_name('detection_masks:0') + num_detections = od_graph.get_tensor_by_name('num_detections:0') + (boxes_np, scores_np, classes_np, keypoints_np, masks_np, + num_detections_np) = sess.run( + [boxes, scores, classes, keypoints, masks, num_detections], + feed_dict={tf_example: tf_example_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_write_graph_and_checkpoint(self): + tmp_dir = self.get_temp_dir() + trained_checkpoint_prefix = os.path.join(tmp_dir, 'model.ckpt') + self._save_checkpoint_from_mock_model(trained_checkpoint_prefix, + use_moving_averages=False) + output_directory = os.path.join(tmp_dir, 'output') + model_path = os.path.join(output_directory, 'model.ckpt') + meta_graph_path = model_path + '.meta' + tf.gfile.MakeDirs(output_directory) + with mock.patch.object( + model_builder, 'build', autospec=True) as mock_builder: + mock_builder.return_value = FakeModel( + add_detection_keypoints=True, add_detection_masks=True) + pipeline_config = pipeline_pb2.TrainEvalPipelineConfig() + pipeline_config.eval_config.use_moving_averages = False + detection_model = model_builder.build(pipeline_config.model, + is_training=False) + exporter.build_detection_graph( + input_type='tf_example', + detection_model=detection_model, + input_shape=None, + output_collection_name='inference_op', + graph_hook_fn=None) + saver = tf.train.Saver() + input_saver_def = saver.as_saver_def() + exporter.write_graph_and_checkpoint( + inference_graph_def=tf.get_default_graph().as_graph_def(), + model_path=model_path, + input_saver_def=input_saver_def, + trained_checkpoint_prefix=trained_checkpoint_prefix) + + tf_example_np = np.hstack([self._create_tf_example( + np.ones((4, 4, 3)).astype(np.uint8))] * 2) + with tf.Graph().as_default() as od_graph: + with self.test_session(graph=od_graph) as sess: + new_saver = tf.train.import_meta_graph(meta_graph_path) + new_saver.restore(sess, model_path) + + tf_example = od_graph.get_tensor_by_name('tf_example:0') + boxes = od_graph.get_tensor_by_name('detection_boxes:0') + scores = od_graph.get_tensor_by_name('detection_scores:0') + raw_boxes = od_graph.get_tensor_by_name('raw_detection_boxes:0') + raw_scores = od_graph.get_tensor_by_name('raw_detection_scores:0') + classes = od_graph.get_tensor_by_name('detection_classes:0') + keypoints = od_graph.get_tensor_by_name('detection_keypoints:0') + masks = od_graph.get_tensor_by_name('detection_masks:0') + num_detections = od_graph.get_tensor_by_name('num_detections:0') + (boxes_np, scores_np, raw_boxes_np, raw_scores_np, classes_np, + keypoints_np, masks_np, num_detections_np) = sess.run( + [boxes, scores, raw_boxes, raw_scores, classes, keypoints, masks, + num_detections], + feed_dict={tf_example: tf_example_np}) + self.assertAllClose(boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.0, 0.0, 0.0]]]) + self.assertAllClose(scores_np, [[0.7, 0.6], + [0.9, 0.0]]) + self.assertAllClose(raw_boxes_np, [[[0.0, 0.0, 0.5, 0.5], + [0.5, 0.5, 0.8, 0.8]], + [[0.5, 0.5, 1.0, 1.0], + [0.0, 0.5, 0.0, 0.5]]]) + self.assertAllClose(raw_scores_np, [[0.7, 0.6], + [0.9, 0.5]]) + self.assertAllClose(classes_np, [[1, 2], + [2, 1]]) + self.assertAllClose(keypoints_np, np.arange(48).reshape([2, 2, 6, 2])) + self.assertAllClose(masks_np, np.arange(64).reshape([2, 2, 4, 4])) + self.assertAllClose(num_detections_np, [2, 1]) + + def test_rewrite_nn_resize_op(self): + g = tf.Graph() + with g.as_default(): + x = array_ops.placeholder(dtypes.float32, shape=(8, 10, 10, 8)) + y = array_ops.placeholder(dtypes.float32, shape=(8, 20, 20, 8)) + s = ops.nearest_neighbor_upsampling(x, 2) + t = s + y + exporter.rewrite_nn_resize_op() + + resize_op_found = False + for op in g.get_operations(): + if op.type == 'ResizeNearestNeighbor': + resize_op_found = True + self.assertEqual(op.inputs[0], x) + self.assertEqual(op.outputs[0].consumers()[0], t.op) + break + + self.assertTrue(resize_op_found) + + def test_rewrite_nn_resize_op_quantized(self): + g = tf.Graph() + with g.as_default(): + x = array_ops.placeholder(dtypes.float32, shape=(8, 10, 10, 8)) + x_conv = slim.conv2d(x, 8, 1) + y = array_ops.placeholder(dtypes.float32, shape=(8, 20, 20, 8)) + s = ops.nearest_neighbor_upsampling(x_conv, 2) + t = s + y + + graph_rewriter_config = graph_rewriter_pb2.GraphRewriter() + graph_rewriter_config.quantization.delay = 500000 + graph_rewriter_fn = graph_rewriter_builder.build( + graph_rewriter_config, is_training=False) + graph_rewriter_fn() + + exporter.rewrite_nn_resize_op(is_quantized=True) + + resize_op_found = False + for op in g.get_operations(): + if op.type == 'ResizeNearestNeighbor': + resize_op_found = True + self.assertEqual(op.inputs[0].op.type, 'FakeQuantWithMinMaxVars') + self.assertEqual(op.outputs[0].consumers()[0], t.op) + break + + self.assertTrue(resize_op_found) + + def test_rewrite_nn_resize_op_odd_size(self): + g = tf.Graph() + with g.as_default(): + x = array_ops.placeholder(dtypes.float32, shape=(8, 10, 10, 8)) + s = ops.nearest_neighbor_upsampling(x, 2) + t = s[:, :19, :19, :] + exporter.rewrite_nn_resize_op() + + resize_op_found = False + for op in g.get_operations(): + if op.type == 'ResizeNearestNeighbor': + resize_op_found = True + self.assertEqual(op.inputs[0], x) + self.assertEqual(op.outputs[0].consumers()[0], t.op) + break + + self.assertTrue(resize_op_found) + + def test_rewrite_nn_resize_op_quantized_odd_size(self): + g = tf.Graph() + with g.as_default(): + x = array_ops.placeholder(dtypes.float32, shape=(8, 10, 10, 8)) + x_conv = slim.conv2d(x, 8, 1) + s = ops.nearest_neighbor_upsampling(x_conv, 2) + t = s[:, :19, :19, :] + + graph_rewriter_config = graph_rewriter_pb2.GraphRewriter() + graph_rewriter_config.quantization.delay = 500000 + graph_rewriter_fn = graph_rewriter_builder.build( + graph_rewriter_config, is_training=False) + graph_rewriter_fn() + + exporter.rewrite_nn_resize_op(is_quantized=True) + + resize_op_found = False + for op in g.get_operations(): + if op.type == 'ResizeNearestNeighbor': + resize_op_found = True + self.assertEqual(op.inputs[0].op.type, 'FakeQuantWithMinMaxVars') + self.assertEqual(op.outputs[0].consumers()[0], t.op) + break + + self.assertTrue(resize_op_found) + + def test_rewrite_nn_resize_op_multiple_path(self): + g = tf.Graph() + with g.as_default(): + with tf.name_scope('nearest_upsampling'): + x = array_ops.placeholder(dtypes.float32, shape=(8, 10, 10, 8)) + x_stack = tf.stack([tf.stack([x] * 2, axis=3)] * 2, axis=2) + x_reshape = tf.reshape(x_stack, [8, 20, 20, 8]) + + with tf.name_scope('nearest_upsampling'): + x_2 = array_ops.placeholder(dtypes.float32, shape=(8, 10, 10, 8)) + x_stack_2 = tf.stack([tf.stack([x_2] * 2, axis=3)] * 2, axis=2) + x_reshape_2 = tf.reshape(x_stack_2, [8, 20, 20, 8]) + + t = x_reshape + x_reshape_2 + + exporter.rewrite_nn_resize_op() + + graph_def = g.as_graph_def() + graph_def = strip_unused_lib.strip_unused( + graph_def, + input_node_names=[ + 'nearest_upsampling/Placeholder', 'nearest_upsampling_1/Placeholder' + ], + output_node_names=['add'], + placeholder_type_enum=dtypes.float32.as_datatype_enum) + + counter_resize_op = 0 + t_input_ops = [op.name for op in t.op.inputs] + for node in graph_def.node: + # Make sure Stacks are replaced. + self.assertNotEqual(node.op, 'Pack') + if node.op == 'ResizeNearestNeighbor': + counter_resize_op += 1 + self.assertIn(six.ensure_str(node.name) + ':0', t_input_ops) + self.assertEqual(counter_resize_op, 2) + + +if __name__ == '__main__': + tf.test.main() diff --git a/workspace/virtuallab/object_detection/g3doc/challenge_evaluation.md b/workspace/virtuallab/object_detection/g3doc/challenge_evaluation.md new file mode 100644 index 0000000..15f032d --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/challenge_evaluation.md @@ -0,0 +1,215 @@ +# Open Images Challenge Evaluation + +The Object Detection API is currently supporting several evaluation metrics used +in the +[Open Images Challenge 2018](https://storage.googleapis.com/openimages/web/challenge.html) +and +[Open Images Challenge 2019](https://storage.googleapis.com/openimages/web/challenge2019.html). +In addition, several data processing tools are available. Detailed instructions +on using the tools for each track are available below. + +**NOTE:** all data links are updated to the Open Images Challenge 2019. + +## Object Detection Track + +The +[Object Detection metric](https://storage.googleapis.com/openimages/web/evaluation.html#object_detection_eval) +protocol requires a pre-processing of the released data to ensure correct +evaluation. The released data contains only leaf-most bounding box annotations +and image-level labels. The evaluation metric implementation is available in the +class `OpenImagesChallengeEvaluator`. + +1. Download + [class hierarchy of Open Images Detection Challenge 2019](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-label500-hierarchy.json) + in JSON format. +2. Download + [ground-truth boundling boxes](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-validation-detection-bbox.csv) + and + [image-level labels](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-validation-detection-human-imagelabels.csv). +3. Run the following command to create hierarchical expansion of the bounding + boxes and image-level label annotations: + +``` +HIERARCHY_FILE=/path/to/challenge-2019-label500-hierarchy.json +BOUNDING_BOXES=/path/to/challenge-2019-validation-detection-bbox +IMAGE_LABELS=/path/to/challenge-2019-validation-detection-human-imagelabels + +python object_detection/dataset_tools/oid_hierarchical_labels_expansion.py \ + --json_hierarchy_file=${HIERARCHY_FILE} \ + --input_annotations=${BOUNDING_BOXES}.csv \ + --output_annotations=${BOUNDING_BOXES}_expanded.csv \ + --annotation_type=1 + +python object_detection/dataset_tools/oid_hierarchical_labels_expansion.py \ + --json_hierarchy_file=${HIERARCHY_FILE} \ + --input_annotations=${IMAGE_LABELS}.csv \ + --output_annotations=${IMAGE_LABELS}_expanded.csv \ + --annotation_type=2 +``` + +1. If you are not using TensorFlow, you can run evaluation directly using your + algorithm's output and generated ground-truth files. {value=4} + +After step 3 you produced the ground-truth files suitable for running 'OID +Challenge Object Detection Metric 2019' evaluation. To run the evaluation, use +the following command: + +``` +INPUT_PREDICTIONS=/path/to/detection_predictions.csv +OUTPUT_METRICS=/path/to/output/metrics/file + +python models/research/object_detection/metrics/oid_challenge_evaluation.py \ + --input_annotations_boxes=${BOUNDING_BOXES}_expanded.csv \ + --input_annotations_labels=${IMAGE_LABELS}_expanded.csv \ + --input_class_labelmap=object_detection/data/oid_object_detection_challenge_500_label_map.pbtxt \ + --input_predictions=${INPUT_PREDICTIONS} \ + --output_metrics=${OUTPUT_METRICS} \ +``` + +Note that predictions file must contain the following keys: +ImageID,LabelName,Score,XMin,XMax,YMin,YMax + +For the Object Detection Track, the participants will be ranked on: + +- "OpenImagesDetectionChallenge_Precision/mAP@0.5IOU" + +To use evaluation within TensorFlow training, use metric name +`oid_challenge_detection_metrics` in the evaluation config. + +## Instance Segmentation Track + +The +[Instance Segmentation metric](https://storage.googleapis.com/openimages/web/evaluation.html#instance_segmentation_eval) +can be directly evaluated using the ground-truth data and model predictions. The +evaluation metric implementation is available in the class +`OpenImagesChallengeEvaluator`. + +1. Download + [class hierarchy of Open Images Instance Segmentation Challenge 2019](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-label300-segmentable-hierarchy.json) + in JSON format. +2. Download + [ground-truth bounding boxes](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-validation-segmentation-bbox.csv) + and + [image-level labels](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-validation-segmentation-labels.csv). +3. Download instance segmentation files for the validation set (see + [Open Images Challenge Downloads page](https://storage.googleapis.com/openimages/web/challenge2019_downloads.html)). + The download consists of a set of .zip archives containing binary .png + masks. + Those should be transformed into a single CSV file in the format: + + ImageID,LabelName,ImageWidth,ImageHeight,XMin,YMin,XMax,YMax,IsGroupOf,Mask + where Mask is MS COCO RLE encoding, compressed with zip, and re-coded with + base64 encoding of a binary mask stored in .png file. See an example + implementation of the encoding function + [here](https://gist.github.com/pculliton/209398a2a52867580c6103e25e55d93c). + +1. Run the following command to create hierarchical expansion of the instance + segmentation, bounding boxes and image-level label annotations: {value=4} + +``` +HIERARCHY_FILE=/path/to/challenge-2019-label300-hierarchy.json +BOUNDING_BOXES=/path/to/challenge-2019-validation-detection-bbox +IMAGE_LABELS=/path/to/challenge-2019-validation-detection-human-imagelabels + +python object_detection/dataset_tools/oid_hierarchical_labels_expansion.py \ + --json_hierarchy_file=${HIERARCHY_FILE} \ + --input_annotations=${BOUNDING_BOXES}.csv \ + --output_annotations=${BOUNDING_BOXES}_expanded.csv \ + --annotation_type=1 + +python object_detection/dataset_tools/oid_hierarchical_labels_expansion.py \ + --json_hierarchy_file=${HIERARCHY_FILE} \ + --input_annotations=${IMAGE_LABELS}.csv \ + --output_annotations=${IMAGE_LABELS}_expanded.csv \ + --annotation_type=2 + +python object_detection/dataset_tools/oid_hierarchical_labels_expansion.py \ + --json_hierarchy_file=${HIERARCHY_FILE} \ + --input_annotations=${INSTANCE_SEGMENTATIONS}.csv \ + --output_annotations=${INSTANCE_SEGMENTATIONS}_expanded.csv \ + --annotation_type=1 +``` + +1. If you are not using TensorFlow, you can run evaluation directly using your + algorithm's output and generated ground-truth files. {value=4} + +``` +INPUT_PREDICTIONS=/path/to/instance_segmentation_predictions.csv +OUTPUT_METRICS=/path/to/output/metrics/file + +python models/research/object_detection/metrics/oid_challenge_evaluation.py \ + --input_annotations_boxes=${BOUNDING_BOXES}_expanded.csv \ + --input_annotations_labels=${IMAGE_LABELS}_expanded.csv \ + --input_class_labelmap=object_detection/data/oid_object_detection_challenge_500_label_map.pbtxt \ + --input_predictions=${INPUT_PREDICTIONS} \ + --input_annotations_segm=${INSTANCE_SEGMENTATIONS}_expanded.csv + --output_metrics=${OUTPUT_METRICS} \ +``` + +Note that predictions file must contain the following keys: +ImageID,ImageWidth,ImageHeight,LabelName,Score,Mask + +Mask must be encoded the same way as groundtruth masks. + +For the Instance Segmentation Track, the participants will be ranked on: + +- "OpenImagesInstanceSegmentationChallenge_Precision/mAP@0.5IOU" + +## Visual Relationships Detection Track + +The +[Visual Relationships Detection metrics](https://storage.googleapis.com/openimages/web/evaluation.html#visual_relationships_eval) +can be directly evaluated using the ground-truth data and model predictions. The +evaluation metric implementation is available in the class +`VRDRelationDetectionEvaluator`,`VRDPhraseDetectionEvaluator`. + +1. Download the ground-truth + [visual relationships annotations](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-validation-vrd.csv) + and + [image-level labels](https://storage.googleapis.com/openimages/challenge_2019/challenge-2019-validation-vrd-labels.csv). +2. Run the follwing command to produce final metrics: + +``` +INPUT_ANNOTATIONS_BOXES=/path/to/challenge-2018-train-vrd.csv +INPUT_ANNOTATIONS_LABELS=/path/to/challenge-2018-train-vrd-labels.csv +INPUT_PREDICTIONS=/path/to/predictions.csv +INPUT_CLASS_LABELMAP=/path/to/oid_object_detection_challenge_500_label_map.pbtxt +INPUT_RELATIONSHIP_LABELMAP=/path/to/relationships_labelmap.pbtxt +OUTPUT_METRICS=/path/to/output/metrics/file + +echo "item { name: '/m/02gy9n' id: 602 display_name: 'Transparent' } +item { name: '/m/05z87' id: 603 display_name: 'Plastic' } +item { name: '/m/0dnr7' id: 604 display_name: '(made of)Textile' } +item { name: '/m/04lbp' id: 605 display_name: '(made of)Leather' } +item { name: '/m/083vt' id: 606 display_name: 'Wooden'} +">>${INPUT_CLASS_LABELMAP} + +echo "item { name: 'at' id: 1 display_name: 'at' } +item { name: 'on' id: 2 display_name: 'on (top of)' } +item { name: 'holds' id: 3 display_name: 'holds' } +item { name: 'plays' id: 4 display_name: 'plays' } +item { name: 'interacts_with' id: 5 display_name: 'interacts with' } +item { name: 'wears' id: 6 display_name: 'wears' } +item { name: 'is' id: 7 display_name: 'is' } +item { name: 'inside_of' id: 8 display_name: 'inside of' } +item { name: 'under' id: 9 display_name: 'under' } +item { name: 'hits' id: 10 display_name: 'hits' } +"> ${INPUT_RELATIONSHIP_LABELMAP} + +python object_detection/metrics/oid_vrd_challenge_evaluation.py \ + --input_annotations_boxes=${INPUT_ANNOTATIONS_BOXES} \ + --input_annotations_labels=${INPUT_ANNOTATIONS_LABELS} \ + --input_predictions=${INPUT_PREDICTIONS} \ + --input_class_labelmap=${INPUT_CLASS_LABELMAP} \ + --input_relationship_labelmap=${INPUT_RELATIONSHIP_LABELMAP} \ + --output_metrics=${OUTPUT_METRICS} +``` + +Note that predictions file must contain the following keys: +ImageID,LabelName1,LabelName2,RelationshipLabel,Score,XMin1,XMax1,YMin1,YMax1,XMin2,XMax2,YMin2,YMax2 + +The participants of the challenge will be evaluated by weighted average of the following three metrics: + +- "VRDMetric_Relationships_mAP@0.5IOU" +- "VRDMetric_Relationships_Recall@50@0.5IOU" +- "VRDMetric_Phrases_mAP@0.5IOU" diff --git a/workspace/virtuallab/object_detection/g3doc/configuring_jobs.md b/workspace/virtuallab/object_detection/g3doc/configuring_jobs.md new file mode 100644 index 0000000..59925f2 --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/configuring_jobs.md @@ -0,0 +1,147 @@ +# Configuring the Object Detection Training Pipeline + +## Overview + +The TensorFlow Object Detection API uses protobuf files to configure the +training and evaluation process. The schema for the training pipeline can be +found in object_detection/protos/pipeline.proto. At a high level, the config +file is split into 5 parts: + +1. The `model` configuration. This defines what type of model will be trained +(ie. meta-architecture, feature extractor). +2. The `train_config`, which decides what parameters should be used to train +model parameters (ie. SGD parameters, input preprocessing and feature extractor +initialization values). +3. The `eval_config`, which determines what set of metrics will be reported for +evaluation. +4. The `train_input_config`, which defines what dataset the model should be +trained on. +5. The `eval_input_config`, which defines what dataset the model will be +evaluated on. Typically this should be different than the training input +dataset. + +A skeleton configuration file is shown below: + +``` +model { +(... Add model config here...) +} + +train_config : { +(... Add train_config here...) +} + +train_input_reader: { +(... Add train_input configuration here...) +} + +eval_config: { +} + +eval_input_reader: { +(... Add eval_input configuration here...) +} +``` + +## Picking Model Parameters + +There are a large number of model parameters to configure. The best settings +will depend on your given application. Faster R-CNN models are better suited to +cases where high accuracy is desired and latency is of lower priority. +Conversely, if processing time is the most important factor, SSD models are +recommended. Read [our paper](https://arxiv.org/abs/1611.10012) for a more +detailed discussion on the speed vs accuracy tradeoff. + +To help new users get started, sample model configurations have been provided +in the object_detection/samples/configs folder. The contents of these +configuration files can be pasted into `model` field of the skeleton +configuration. Users should note that the `num_classes` field should be changed +to a value suited for the dataset the user is training on. + +## Defining Inputs + +The TensorFlow Object Detection API accepts inputs in the TFRecord file format. +Users must specify the locations of both the training and evaluation files. +Additionally, users should also specify a label map, which define the mapping +between a class id and class name. The label map should be identical between +training and evaluation datasets. + +An example input configuration looks as follows: + +``` +tf_record_input_reader { + input_path: "/usr/home/username/data/train.record" +} +label_map_path: "/usr/home/username/data/label_map.pbtxt" +``` + +Users should substitute the `input_path` and `label_map_path` arguments and +insert the input configuration into the `train_input_reader` and +`eval_input_reader` fields in the skeleton configuration. Note that the paths +can also point to Google Cloud Storage buckets (ie. +"gs://project_bucket/train.record") for use on Google Cloud. + +## Configuring the Trainer + +The `train_config` defines parts of the training process: + +1. Model parameter initialization. +2. Input preprocessing. +3. SGD parameters. + +A sample `train_config` is below: + +``` +batch_size: 1 +optimizer { + momentum_optimizer: { + learning_rate: { + manual_step_learning_rate { + initial_learning_rate: 0.0002 + schedule { + step: 0 + learning_rate: .0002 + } + schedule { + step: 900000 + learning_rate: .00002 + } + schedule { + step: 1200000 + learning_rate: .000002 + } + } + } + momentum_optimizer_value: 0.9 + } + use_moving_average: false +} +fine_tune_checkpoint: "/usr/home/username/tmp/model.ckpt-#####" +from_detection_checkpoint: true +load_all_detection_checkpoint_vars: true +gradient_clipping_by_norm: 10.0 +data_augmentation_options { + random_horizontal_flip { + } +} +``` + +### Input Preprocessing + +The `data_augmentation_options` in `train_config` can be used to specify +how training data can be modified. This field is optional. + +### SGD Parameters + +The remainings parameters in `train_config` are hyperparameters for gradient +descent. Please note that the optimal learning rates provided in these +configuration files may depend on the specifics of the training setup (e.g. +number of workers, gpu type). + +## Configuring the Evaluator + +The main components to set in `eval_config` are `num_examples` and +`metrics_set`. The parameter `num_examples` indicates the number of batches ( +currently of batch size 1) used for an evaluation cycle, and often is the total +size of the evaluation dataset. The parameter `metrics_set` indicates which +metrics to run during evaluation (i.e. `"coco_detection_metrics"`). diff --git a/workspace/virtuallab/object_detection/g3doc/context_rcnn.md b/workspace/virtuallab/object_detection/g3doc/context_rcnn.md new file mode 100644 index 0000000..14a42d8 --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/context_rcnn.md @@ -0,0 +1,201 @@ +# Context R-CNN + +[![TensorFlow 1.15](https://img.shields.io/badge/TensorFlow-1.15-FF6F00?logo=tensorflow)](https://github.com/tensorflow/tensorflow/releases/tag/v1.15.0) + +Context R-CNN is an object detection model that uses contextual features to +improve object detection. See https://arxiv.org/abs/1912.03538 for more details. + +## Table of Contents + +* [Preparing Context Data for Context R-CNN](#preparing-context-data-for-context-r-cnn) + + [Generating TfRecords from a set of images and a COCO-CameraTraps style + JSON](#generating-tfrecords-from-a-set-of-images-and-a-coco-cameratraps-style-json) + + [Generating weakly-supervised bounding box labels for image-labeled data](#generating-weakly-supervised-bounding-box-labels-for-image-labeled-data) + + [Generating and saving contextual features for each image](#generating-and-saving-contextual-features-for-each-image) + + [Building up contextual memory banks and storing them for each context + group](#building-up-contextual-memory-banks-and-storing-them-for-each-context-group) +- [Training a Context R-CNN Model](#training-a-context-r-cnn-model) +- [Exporting a Context R-CNN Model](#exporting-a-context-r-cnn-model) + +## Preparing Context Data for Context R-CNN + +In this section, we will walk through the process of generating TfRecords with +contextual features. We focus on building context from object-centric features +generated with a pre-trained Faster R-CNN model, but you can adapt the provided +code to use alternative feature extractors. + +Each of these data processing scripts uses Apache Beam, which can be installed +using + +``` +pip install apache-beam +``` + +and can be run locally, or on a cluster for efficient processing of large +amounts of data. Note that generate_detection_data.py and +generate_embedding_data.py both involve running inference, and may be very slow +to run locally. See the +[Apache Beam documentation](https://beam.apache.org/documentation/runners/dataflow/) +for more information, and Google Cloud Documentation for a tutorial on +[running Beam jobs on DataFlow](https://cloud.google.com/dataflow/docs/quickstarts/quickstart-python). + +### Generating TfRecords from a set of images and a COCO-CameraTraps style JSON + +If your data is already stored in TfRecords, you can skip this first step. + +We assume a COCO-CameraTraps json format, as described on +[LILA.science](https://github.com/microsoft/CameraTraps/blob/master/data_management/README.md). + +COCO-CameraTraps is a format that adds static-camera-specific fields, such as a +location ID and datetime, to the well-established COCO format. To generate +appropriate context later on, be sure you have specified each contextual group +with a different location ID, which in the static camera case would be the ID of +the camera, as well as the datetime each photo was taken. We assume that empty +images will be labeled 'empty' with class id 0. + +To generate TfRecords from your database and local image folder, run + +``` +python object_detection/dataset_tools/context_rcnn/create_cococameratraps_tfexample_main.py \ + --alsologtostderr \ + --output_tfrecord_prefix="/path/to/output/tfrecord/location/prefix" \ + --image_directory="/path/to/image/folder/" \ + --input_annotations_file="path/to/annotations.json" +``` + +### Generating weakly-supervised bounding box labels for image-labeled data + +If all your data already has bounding box labels you can skip this step. + +Many camera trap datasets do not have bounding box labels, or only have bounding +box labels for some of the data. We have provided code to add bounding boxes +from a pretrained model (such as the +[Microsoft AI for Earth MegaDetector](https://github.com/microsoft/CameraTraps/blob/master/megadetector.md)) +and match the boxes to the image-level class label. + +To export your pretrained detection model, run + +``` +python object_detection/export_inference_graph.py \ + --alsologtostderr \ + --input_type tf_example \ + --pipeline_config_path path/to/faster_rcnn_model.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory +``` + +To add bounding boxes to your dataset using the above model, run + +``` +python object_detection/dataset_tools/context_rcnn/generate_detection_data.py \ + --alsologtostderr \ + --input_tfrecord path/to/input_tfrecord@X \ + --output_tfrecord path/to/output_tfrecord@X \ + --model_dir path/to/exported_model_directory/saved_model +``` + +If an image already has bounding box labels, those labels are left unchanged. If +an image is labeled 'empty' (class ID 0), we will not generate boxes for that +image. + +### Generating and saving contextual features for each image + +We next extract and store features for each image from a pretrained model. This +model can be the same model as above, or be a class-specific detection model +trained on data from your classes of interest. + +To export your pretrained detection model, run + +``` +python object_detection/export_inference_graph.py \ + --alsologtostderr \ + --input_type tf_example \ + --pipeline_config_path path/to/pipeline.config \ + --trained_checkpoint_prefix path/to/model.ckpt \ + --output_directory path/to/exported_model_directory \ + --additional_output_tensor_names detection_features +``` + +Make sure that you have set `output_final_box_features: true` within +your config file before exporting. This is needed to export the features as an +output, but it does not need to be set during training. + +To generate and save contextual features for your data, run + +``` +python object_detection/dataset_tools/context_rcnn/generate_embedding_data.py \ + --alsologtostderr \ + --embedding_input_tfrecord path/to/input_tfrecords* \ + --embedding_output_tfrecord path/to/output_tfrecords \ + --embedding_model_dir path/to/exported_model_directory/saved_model +``` + +### Building up contextual memory banks and storing them for each context group + +To build the context features you just added for each image into memory banks, +run + +``` +python object_detection/dataset_tools/context_rcnn/add_context_to_examples.py \ + --input_tfrecord path/to/input_tfrecords* \ + --output_tfrecord path/to/output_tfrecords \ + --sequence_key image/location \ + --time_horizon month +``` + +where the input_tfrecords for add_context_to_examples.py are the +output_tfrecords from generate_embedding_data.py. + +For all options, see add_context_to_examples.py. By default, this code builds +TfSequenceExamples, which are more data efficient (this allows you to store the +context features once for each context group, as opposed to once per image). If +you would like to export TfExamples instead, set flag `--output_type +tf_example`. + +If you use TfSequenceExamples, you must be sure to set `input_type: +TF_SEQUENCE_EXAMPLE` within your Context R-CNN configs for both +train_input_reader and test_input_reader. See +`object_detection/test_data/context_rcnn_camera_trap.config` +for an example. + +## Training a Context R-CNN Model + +To train a Context R-CNN model, you must first set up your config file. See +`test_data/context_rcnn_camera_trap.config` for an example. The important +difference between this config and a Faster R-CNN config is the inclusion of a +`context_config` within the model, which defines the necessary Context R-CNN +parameters. + +``` +context_config { + max_num_context_features: 2000 + context_feature_length: 2057 + } +``` + +Once your config file has been updated with your local paths, you can follow +along with documentation for running [locally](running_locally.md), or +[on the cloud](running_on_cloud.md). + +## Exporting a Context R-CNN Model + +Since Context R-CNN takes context features as well as images as input, we have +to explicitly define the other inputs ("side_inputs") to the model when +exporting, as below. This example is shown with default context feature shapes. + +``` +python export_inference_graph.py \ + --input_type image_tensor \ + --input_shape 1,-1,-1,3 \ + --pipeline_config_path /path/to/context_rcnn_model/pipeline.config \ + --trained_checkpoint_prefix /path/to/context_rcnn_model/model.ckpt \ + --output_directory /path/to/output_directory \ + --use_side_inputs True \ + --side_input_shapes 1,2000,2057/1 \ + --side_input_names context_features,valid_context_size \ + --side_input_types float,int + +``` + +If you have questions about Context R-CNN, please contact +[Sara Beery](https://beerys.github.io/). diff --git a/workspace/virtuallab/object_detection/g3doc/defining_your_own_model.md b/workspace/virtuallab/object_detection/g3doc/defining_your_own_model.md new file mode 100644 index 0000000..dabc064 --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/defining_your_own_model.md @@ -0,0 +1,137 @@ +# So you want to create a new model! + +In this section, we discuss some of the abstractions that we use +for defining detection models. If you would like to define a new model +architecture for detection and use it in the TensorFlow Detection API, +then this section should also serve as a high level guide to the files that you +will need to edit to get your new model working. + +## DetectionModels (`object_detection/core/model.py`) + +In order to be trained, evaluated, and exported for serving using our +provided binaries, all models under the TensorFlow Object Detection API must +implement the `DetectionModel` interface (see the full definition in `object_detection/core/model.py`). In particular, +each of these models are responsible for implementing 5 functions: + +* `preprocess`: Run any preprocessing (e.g., scaling/shifting/reshaping) of + input values that is necessary prior to running the detector on an input + image. +* `predict`: Produce “raw” prediction tensors that can be passed to loss or + postprocess functions. +* `postprocess`: Convert predicted output tensors to final detections. +* `loss`: Compute scalar loss tensors with respect to provided groundtruth. +* `restore`: Load a checkpoint into the TensorFlow graph. + +Given a `DetectionModel` at training time, we pass each image batch through +the following sequence of functions to compute a loss which can be optimized via +SGD: + +``` +inputs (images tensor) -> preprocess -> predict -> loss -> outputs (loss tensor) +``` + +And at eval time, we pass each image batch through the following sequence of +functions to produce a set of detections: + +``` +inputs (images tensor) -> preprocess -> predict -> postprocess -> + outputs (boxes tensor, scores tensor, classes tensor, num_detections tensor) +``` + +Some conventions to be aware of: + +* `DetectionModel`s should make no assumptions about the input size or aspect + ratio --- they are responsible for doing any resize/reshaping necessary + (see docstring for the `preprocess` function). +* Output classes are always integers in the range `[0, num_classes)`. + Any mapping of these integers to semantic labels is to be handled outside + of this class. We never explicitly emit a “background class” --- thus 0 is + the first non-background class and any logic of predicting and removing + implicit background classes must be handled internally by the implementation. +* Detected boxes are to be interpreted as being in + `[y_min, x_min, y_max, x_max]` format and normalized relative to the + image window. +* We do not specifically assume any kind of probabilistic interpretation of the + scores --- the only important thing is their relative ordering. Thus + implementations of the postprocess function are free to output logits, + probabilities, calibrated probabilities, or anything else. + +## Defining a new Faster R-CNN or SSD Feature Extractor + +In most cases, you probably will not implement a `DetectionModel` from scratch +--- instead you might create a new feature extractor to be used by one of the +SSD or Faster R-CNN meta-architectures. (We think of meta-architectures as +classes that define entire families of models using the `DetectionModel` +abstraction). + +Note: For the following discussion to make sense, we recommend first becoming +familiar with the [Faster R-CNN](https://arxiv.org/abs/1506.01497) paper. + +Let’s now imagine that you have invented a brand new network architecture +(say, “InceptionV100”) for classification and want to see how InceptionV100 +would behave as a feature extractor for detection (say, with Faster R-CNN). +A similar procedure would hold for SSD models, but we’ll discuss Faster R-CNN. + +To use InceptionV100, we will have to define a new +`FasterRCNNFeatureExtractor` and pass it to our `FasterRCNNMetaArch` +constructor as input. See +`object_detection/meta_architectures/faster_rcnn_meta_arch.py` for definitions +of `FasterRCNNFeatureExtractor` and `FasterRCNNMetaArch`, respectively. +A `FasterRCNNFeatureExtractor` must define a few +functions: + +* `preprocess`: Run any preprocessing of input values that is necessary prior + to running the detector on an input image. +* `_extract_proposal_features`: Extract first stage Region Proposal Network + (RPN) features. +* `_extract_box_classifier_features`: Extract second stage Box Classifier + features. +* `restore_from_classification_checkpoint_fn`: Load a checkpoint into the + TensorFlow graph. + +See the `object_detection/models/faster_rcnn_resnet_v1_feature_extractor.py` +definition as one example. Some remarks: + +* We typically initialize the weights of this feature extractor + using those from the + [Slim Resnet-101 classification checkpoint](https://github.com/tensorflow/models/tree/master/research/slim#pre-trained-models), + and we know + that images were preprocessed when training this checkpoint + by subtracting a channel mean from each input + image. Thus, we implement the preprocess function to replicate the same + channel mean subtraction behavior. +* The “full” resnet classification network defined in slim is cut into two + parts --- all but the last “resnet block” is put into the + `_extract_proposal_features` function and the final block is separately + defined in the `_extract_box_classifier_features function`. In general, + some experimentation may be required to decide on an optimal layer at + which to “cut” your feature extractor into these two pieces for Faster R-CNN. + +## Register your model for configuration + +Assuming that your new feature extractor does not require nonstandard +configuration, you will want to ideally be able to simply change the +“feature_extractor.type” fields in your configuration protos to point to a +new feature extractor. In order for our API to know how to understand this +new type though, you will first have to register your new feature +extractor with the model builder (`object_detection/builders/model_builder.py`), +whose job is to create models from config protos.. + +Registration is simple --- just add a pointer to the new Feature Extractor +class that you have defined in one of the SSD or Faster R-CNN Feature +Extractor Class maps at the top of the +`object_detection/builders/model_builder.py` file. +We recommend adding a test in `object_detection/builders/model_builder_test.py` +to make sure that parsing your proto will work as expected. + +## Taking your new model for a spin + +After registration you are ready to go with your model! Some final tips: + +* To save time debugging, try running your configuration file locally first + (both training and evaluation). +* Do a sweep of learning rates to figure out which learning rate is best + for your model. +* A small but often important detail: you may find it necessary to disable + batchnorm training (that is, load the batch norm parameters from the + classification checkpoint, but do not update them during gradient descent). diff --git a/workspace/virtuallab/object_detection/g3doc/evaluation_protocols.md b/workspace/virtuallab/object_detection/g3doc/evaluation_protocols.md new file mode 100644 index 0000000..d5a070f --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/evaluation_protocols.md @@ -0,0 +1,163 @@ +# Supported object detection evaluation protocols + +The TensorFlow Object Detection API currently supports three evaluation protocols, +that can be configured in `EvalConfig` by setting `metrics_set` to the +corresponding value. + +## PASCAL VOC 2010 detection metric + +`EvalConfig.metrics_set='pascal_voc_detection_metrics'` + +The commonly used mAP metric for evaluating the quality of object detectors, +computed according to the protocol of the PASCAL VOC Challenge 2010-2012. The +protocol is available +[here](http://host.robots.ox.ac.uk/pascal/VOC/voc2010/devkit_doc_08-May-2010.pdf). + +## Weighted PASCAL VOC detection metric + +`EvalConfig.metrics_set='weighted_pascal_voc_detection_metrics'` + +The weighted PASCAL metric computes the mean average precision as the average +precision when treating all classes as a single class. In comparison, +PASCAL metrics computes the mean average precision as the mean of the +per-class average precisions. + +For example, the test set consists of two classes, "cat" and "dog", and there +are ten times more boxes of "cat" than those of "dog". According to PASCAL VOC +2010 metric, performance on each of the two classes would contribute equally +towards the final mAP value, while for the Weighted PASCAL VOC metric the final +mAP value will be influenced by frequency of each class. + +## PASCAL VOC 2010 instance segmentation metric + +`EvalConfig.metrics_set='pascal_voc_instance_segmentation_metrics'` + +Similar to Pascal VOC 2010 detection metric, but computes the intersection over +union based on the object masks instead of object boxes. + +## Weighted PASCAL VOC instance segmentation metric + +`EvalConfig.metrics_set='weighted_pascal_voc_instance_segmentation_metrics'` + +Similar to the weighted pascal voc 2010 detection metric, but computes the +intersection over union based on the object masks instead of object boxes. + + +## COCO detection metrics + +`EvalConfig.metrics_set='coco_detection_metrics'` + +The COCO metrics are the official detection metrics used to score the +[COCO competition](http://cocodataset.org/) and are similar to Pascal VOC +metrics but have a slightly different implementation and report additional +statistics such as mAP at IOU thresholds of .5:.95, and precision/recall +statistics for small, medium, and large objects. +See the +[pycocotools](https://github.com/cocodataset/cocoapi/tree/master/PythonAPI) +repository for more details. + +## COCO mask metrics + +`EvalConfig.metrics_set='coco_mask_metrics'` + +Similar to the COCO detection metrics, but computes the +intersection over union based on the object masks instead of object boxes. + +## Open Images V2 detection metric + +`EvalConfig.metrics_set='oid_V2_detection_metrics'` + +This metric is defined originally for evaluating detector performance on [Open +Images V2 dataset](https://github.com/openimages/dataset) and is fairly similar +to the PASCAL VOC 2010 metric mentioned above. It computes interpolated average +precision (AP) for each class and averages it among all classes (mAP). + +The difference to the PASCAL VOC 2010 metric is the following: Open Images +annotations contain `group-of` ground-truth boxes (see [Open Images data +description](https://github.com/openimages/dataset#annotations-human-bboxcsv)), +that are treated differently for the purpose of deciding whether detections are +"true positives", "ignored", "false positives". Here we define these three +cases: + +A detection is a "true positive" if there is a non-group-of ground-truth box, +such that: + +* The detection box and the ground-truth box are of the same class, and + intersection-over-union (IoU) between the detection box and the ground-truth + box is greater than the IoU threshold (default value 0.5). \ + Illustration of handling non-group-of boxes: \ + ![alt + groupof_case_eval](img/nongroupof_case_eval.png "illustration of handling non-group-of boxes: yellow box - ground truth bounding box; green box - true positive; red box - false positives.") + + * yellow box - ground-truth box; + * green box - true positive; + * red boxes - false positives. + +* This is the highest scoring detection for this ground truth box that + satisfies the criteria above. + +A detection is "ignored" if it is not a true positive, and there is a `group-of` +ground-truth box such that: + +* The detection box and the ground-truth box are of the same class, and the + area of intersection between the detection box and the ground-truth box + divided by the area of the detection is greater than 0.5. This is intended + to measure whether the detection box is approximately inside the group-of + ground-truth box. \ + Illustration of handling `group-of` boxes: \ + ![alt + groupof_case_eval](img/groupof_case_eval.png "illustration of handling group-of boxes: yellow box - ground truth bounding box; grey boxes - two detections of cars, that are ignored; red box - false positive.") + + * yellow box - ground-truth box; + * grey boxes - two detections on cars, that are ignored; + * red box - false positive. + +A detection is a "false positive" if it is neither a "true positive" nor +"ignored". + +Precision and recall are defined as: + +* Precision = number-of-true-positives/(number-of-true-positives + number-of-false-positives) +* Recall = number-of-true-positives/number-of-non-group-of-boxes + +Note that detections ignored as firing on a `group-of` ground-truth box do not +contribute to the number of true positives. + +The labels in Open Images are organized in a +[hierarchy](https://storage.googleapis.com/openimages/2017_07/bbox_labels_vis/bbox_labels_vis.html). +Ground-truth bounding-boxes are annotated with the most specific class available +in the hierarchy. For example, "car" has two children "limousine" and "van". Any +other kind of car is annotated as "car" (for example, a sedan). Given this +convention, the evaluation software treats all classes independently, ignoring +the hierarchy. To achieve high performance values, object detectors should +output bounding-boxes labelled in the same manner. + +The old metric name is DEPRECATED. +`EvalConfig.metrics_set='open_images_V2_detection_metrics'` + +## OID Challenge Object Detection Metric + +`EvalConfig.metrics_set='oid_challenge_detection_metrics'` + +The metric for the OID Challenge Object Detection Metric 2018/2019 Object +Detection track. The description is provided on the +[Open Images Challenge website](https://storage.googleapis.com/openimages/web/evaluation.html#object_detection_eval). + +The old metric name is DEPRECATED. +`EvalConfig.metrics_set='oid_challenge_object_detection_metrics'` + +## OID Challenge Visual Relationship Detection Metric + +The metric for the OID Challenge Visual Relationship Detection Metric 2018,2019 +Visual Relationship Detection track. The description is provided on the +[Open Images Challenge website](https://storage.googleapis.com/openimages/web/evaluation.html#visual_relationships_eval). +Note: this is currently a stand-alone metric, that can be used only through the +`metrics/oid_vrd_challenge_evaluation.py` util. + +## OID Challenge Instance Segmentation Metric + +`EvalConfig.metrics_set='oid_challenge_segmentation_metrics'` + +The metric for the OID Challenge Instance Segmentation Metric 2019, Instance +Segmentation track. The description is provided on the +[Open Images Challenge website](https://storage.googleapis.com/openimages/web/evaluation.html#instance_segmentation_eval). diff --git a/workspace/virtuallab/object_detection/g3doc/exporting_models.md b/workspace/virtuallab/object_detection/g3doc/exporting_models.md new file mode 100644 index 0000000..701acf3 --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/exporting_models.md @@ -0,0 +1,38 @@ +# Exporting a trained model for inference + +[![TensorFlow 1.15](https://img.shields.io/badge/TensorFlow-1.15-FF6F00?logo=tensorflow)](https://github.com/tensorflow/tensorflow/releases/tag/v1.15.0) + +After your model has been trained, you should export it to a TensorFlow +graph proto. A checkpoint will typically consist of three files: + +* model.ckpt-${CHECKPOINT_NUMBER}.data-00000-of-00001 +* model.ckpt-${CHECKPOINT_NUMBER}.index +* model.ckpt-${CHECKPOINT_NUMBER}.meta + +After you've identified a candidate checkpoint to export, run the following +command from tensorflow/models/research: + +``` bash +# From tensorflow/models/research/ +INPUT_TYPE=image_tensor +PIPELINE_CONFIG_PATH={path to pipeline config file} +TRAINED_CKPT_PREFIX={path to model.ckpt} +EXPORT_DIR={path to folder that will be used for export} +python object_detection/export_inference_graph.py \ + --input_type=${INPUT_TYPE} \ + --pipeline_config_path=${PIPELINE_CONFIG_PATH} \ + --trained_checkpoint_prefix=${TRAINED_CKPT_PREFIX} \ + --output_directory=${EXPORT_DIR} +``` + +NOTE: We are configuring our exported model to ingest 4-D image tensors. We can +also configure the exported model to take encoded images or serialized +`tf.Example`s. + +After export, you should see the directory ${EXPORT_DIR} containing the following: + +* saved_model/, a directory containing the saved model format of the exported model +* frozen_inference_graph.pb, the frozen graph format of the exported model +* model.ckpt.*, the model checkpoints used for exporting +* checkpoint, a file specifying to restore included checkpoint files +* pipeline.config, pipeline config file for the exported model diff --git a/workspace/virtuallab/object_detection/g3doc/faq.md b/workspace/virtuallab/object_detection/g3doc/faq.md new file mode 100644 index 0000000..f2a6e30 --- /dev/null +++ b/workspace/virtuallab/object_detection/g3doc/faq.md @@ -0,0 +1,27 @@ +# Frequently Asked Questions + +## Q: How can I ensure that all the groundtruth boxes are used during train and eval? +A: For the object detecion framework to be TPU-complient, we must pad our input +tensors to static shapes. This means that we must pad to a fixed number of +bounding boxes, configured by `InputReader.max_number_of_boxes`. It is +important to set this value to a number larger than the maximum number of +groundtruth boxes in the dataset. If an image is encountered with more +bounding boxes, the excess boxes will be clipped. + +## Q: AttributeError: 'module' object has no attribute 'BackupHandler' +A: This BackupHandler (tf_slim.tfexample_decoder.BackupHandler) was +introduced in tensorflow 1.5.0 so runing with earlier versions may cause this +issue. It now has been replaced by +object_detection.data_decoders.tf_example_decoder.BackupHandler. Whoever sees +this issue should be able to resolve it by syncing your fork to HEAD. +Same for LookupTensor. + +## Q: AttributeError: 'module' object has no attribute 'LookupTensor' +A: Similar to BackupHandler, syncing your fork to HEAD should make it work. + +## Q: Why can't I get the inference time as reported in model zoo? +A: The inference time reported in model zoo is mean time of testing hundreds of +images with an internal machine. As mentioned in +[TensorFlow detection model zoo](tf1_detection_zoo.md), this speed depends +highly on one's specific hardware configuration and should be treated more as +relative timing. diff --git a/workspace/virtuallab/object_detection/g3doc/img/dogs_detections_output.jpg b/workspace/virtuallab/object_detection/g3doc/img/dogs_detections_output.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9e88a7010fa90f5c4a74f6caee78f5c975f77e40 GIT binary patch literal 372894 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

    sjYXQID<)38 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νdgK;AU{OM9%y) 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�V`*B&}JIse0-GP_4pz`4?vD1^D3?m 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/g3doc/img/groupof_case_eval.png b/workspace/virtuallab/object_detection/g3doc/img/groupof_case_eval.png new file mode 100644 index 0000000000000000000000000000000000000000..5abc9b6984fb5816ca4f2e6f40e38ec6e6ea9cfc GIT binary patch literal 242500 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 pDvuR0c~Z)CG{$O>U~P2V;SLtQvV0tDj{F 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

    8m^u+ z&11vzYB4mwxf7o(Mlp=>f=_zP)&3f2I)&cNHp~cBWe<{Lha@*o!|Pn$!Z4bRt#9k8tSiZT*ZfWk{t>?wtfisTrjRk+<-2WI{kW$O z4S4$cV{vPuM`p@4Z@8E~eeqs`{wcDKL2qrQUdY>uyO@AhKA@gBt*sYXdn;)qxA4uK zz;Vl6C(b>`y=d^zp3~F%@+H@;2<&+^m%`0h$SyTGp6YDyh{a=HrCv`6YZirkh-PxU z7{Xz3-n}Jt#gg6aX45Sgt7poOA%Ofk)rfpkcW@FL4~OY$#;S#m(t^dK{#B z=9O!1(CE>dA_?X@FENPq#~N5!cf7w%XAso zpHQn%>H4E-t^WWMJ#s@iI59&3j1%;)L$XzfDh)Wmq;S7`9^6x|G?P5z%doZp9Fr`0 zZ2fA!t@g?K#1iGP$y;jLlyKK|94C&V2*IEu=~U8;8PQH*{h)^*jKwsQH&u4HTh;8p9- zO0v3sophuAC3$1RIxi-&bt>~E9R{OGD?LdE#5hAVQEIZsARD%z3|r}nBlwS}NUWNs zr!i+CaFMbyk6hJe@Lz`RCG%yra^UU`7j8XsimP$qyF^{Kx{aJ}NynZr$?kJkdiSw> zxM`~$CXwPl2kOPF?YvC>Y<#j#K_k;S#cJExXzT>Dvdn*ktbgHNZ~p)Y2EXSEi1n>K z!Q*m9GDoKsxuj`+Ak)B4?Ir*W5=Hlw2>t;3vsjj|u7*J%id?4+f`{wHNY>wKWqZvtEI)~4BCwa|I`d!qi<)zyJfwO)Xiq+J8h)OZ* zp<$)AlNTRuJ6L}U4oy;?-p1Kkq)XY~ISIIu)g+oWpsHhz;T#TCf`N*Sv^_}2v9(!2 z2jwx5$)uZ>=%nKwrD*PFi2_BSI~?$3kdN|c`);=@EPvSWfw+9plgGFJ0A8eR4_Z#T z#KdQ^j(uu)wB1j6X)ff*7+^(9Q|uhjGw(Go6I!qfqn=wB!64KU&*RAmTN4BdMi?@3 z2e7Tg)7_Mr=9z#w;mFUowLDsxWy`$YnJ29hQ~OUq=8d#Sz!v^Co;~+Ez>JRSIWrntbu}uJYWMt0jcaRU zb069IoD!-wt9LKU#GCeleE=0BUtHQ?%@7AaF=l2Wx-n+6FklD(ow*%Pr8edrF4!wF zu3Lr&6>1s~>L%>bf;>$+rr0#A`4~Poj0OBN+Lg6^Uf>b^j=>jlk&=CdL%y1gPR_|0PIaQ96!7)Bq3OAYKJ_Em-bjW?ZET~@ z1OzO8m10}jFpe3AIdWuC#yWj!8MSF5U*4jsto3%HmmzKaTK*Ik3fvk)9WxR>l12uyBEp0xd zGqRuFJaXqe_TrXp7e&=&bX{`vKRc8=E@~k)E3}5^ zEkBxVw{ya(XF0BDcoRU=qzyNUrw#{ME*m)fYVDqrV*%Z7;tQGa(5(6V1s>9Crlx9| zTS=cDf5E|#>Cu0|EqrQiEdXZMbWF%oX&>qF@n6pEe?wpJa7~*>{{RHP@tlt|vTfG% zO|0_t&HQ~ue>}D7;Qs)H^E}Lby|TAs|JM3X{t4?OtNSq|aZHh~{3qTpEZ79+{t`Vx zz1Ay{6k*=~06w56Z)*A1{t5ACs3+{!U+yb>LGh6Uw@`dZsN%iSEi(DiHZ$}G=4|z^ z$uaV$_1dedll;%uFpy6PPhN}iHQ!d#_8B9c%WW7WV4i&{7`#Vk^L?U8iN*q{B-FF`TIXnK3r2^j2u^FA*5+CxcNnF)b$n%PnQyU8wGId}U>~hA zUmYPrVbfK4=gfYUn=XPQ+|t|?0l6d`4@#|bqiL!(US5_5w(N6CQfeyW_EhYR5BN*w zoGU_+5P!N)6wAL6UdgyUuqAponbx?iVQjShLB^A*4=DZR&mhzEd!27rCf?H4S&mAE z3^*U1Wal?7SgZPKU$RMaY?0Ai>sJV@2z3Q>yMik^_U_8%6~upeNM_r>H6!?c#g3kD z_9WO<0fPs!B6~g@IUst_}acV@CKu- zNA^89Y4cxa%ZltHWnMKrs06Uj8>{3y{{VsB7#fOLMdNK&N%|N(;M-L9JaJ#@R|{mh zhFg%(r&5nCM4s(wcHe%tKQ7`PAgS#WJ_HXd7%g^{D z&x*fipM-k0q2WDBT}ixOscFj^-rimAx1Qc6B&-RXM#DK}<2lbY`XQ!#LC_=A?d^15 z62%?l=w8y?NEO^KB!QlP3jD{!Y_l+idn)y4$=gxe-44A=~Q?tLrG{w8a8diRJUzP^)uqQw!L*ACr%YtX(6{4QS>co{YHYtr!Q zGayJ2Sx5|f9`*iza0S=WKRNbmrEmQzCxOGMFB9=Kto%IG7gqZ{yt;%jx5*TS zNaOQ8mm{@uz9aA##9Qr7Z7J`r-Z;XmOLib|M{T_cKK1=+JVjXE>+U~rjS5n;Oo!qh zkM(<7dwA|4w~~8=4v^{7sD%!BEZsn^GhgvGt*dE`75@N>^*F7Afd#g~G9d?+TylRJ zXT(2*{wDA}!Wea1D~M%{i8`AsZp?dOPs}Tb2T-)l)RJx5NX$9fPq)&%O4Vg4N;Msi za}P>B%}N%3*O`9kYPNRr#SO-zGBky^n-&Ta`^2wL#-g&+ZfCWTV%DNkcri-Hr(=`W zk}rwAEz_-Lvxif;NGFf?%@YhrG0^6*uy$Ds`96AA8(j@GFi`rlao4=Ua88 zxn&!9n&JFUZ)}jkeWe|(@;M~qKHce;+J2*Da4+9Wu$;aNlNccSkOzNijybL6CG6}E znZWZL;c?oflJ?cIjJVTyd#5!;o9 zc3{7F1zd;CREt-JCHqs_scnRyG86dY`c+Hc9!X^&T|Y^8V!u3?;8yM7v{Ft z^DWG>wZw-dyw%#i!_?P2v5-?%Px3TaX5)5tf71VD_m_rF}GeqL>*Y zJ3>hwITghD+TGOcX%48C+kfSqtOqCYHM68>y0x50w-zN#Pf z6?V>nHC<&B&b!l^LUK%2 zi>gn%J8}NAImLD=)l*LAD}*JdE!l8t*EcGgG>JxeEb@R?>+MgL?)6bcwTxQ<%NZ^J z!S$?pH3`~t6}_^E<()CdTI9$%-dpe({a6qx@?{S+@CEoTQ<%dek#`dre%olE-)6R?P!TFhEk)Sgp2W zb`ImuZfZHEr+%KG<4r3jbUqsRiLdxM*m!4HmexrSB+|rG6VFgc$f$fz@dL&_B5yZP z@fM|ZladWnI*bQ)#Tfff5N6yuHn9&hN;3b z<`+@%2acuIqt>nbM|PJnro@}=jh(*ZBpRzHj(#{=Sj(nE@iye!%n5k5FC?7jIONe4 zw~FA5nPd*60EEN`KBkuC^|+V!-Bk`3KBwBb>T=FI%Krc}qAoGEi|{h^&k%e> zwGC$eHk~7qGv>IAJ4!p2J1KO(dp3nZ7hT{?Ff1b%YAz8 zdxK+jn52vY`^cSv{6$1{jXL?={>8kj@KQD${v(>!H7~Q!n?0G+TWX#cOKFz-#L(GK zw{&)f6TQxG2cb32%i`4j)eCiX4b7^tT(l)v0n?G%tB9>6zB84Ws-PZBu=n;j;+s4+ zS0!<2aT-S3ytf$y)KsrNmMWbpS8VC@?P?2&1;v_Kx*wOylcKj~2S1%?UwD$nc;Ob> zBoVTWv&yNE&5lkFO5`mhNfbsj(z(cH2N=hwtCL+@+eA0oF;a3s=xd@CF09O*8h6m$ zxYdkN?KZhYAAA5Z4OWi&*<~^%?Aa-`i3`)#vTZd9E~hfbr=zQJjJ9$Z7m2~x74Ez+&7l!7C;q`Tm>wCovF8aRk4&! z4aLdN-XV?$`Sh#y&80%h86#56esy2d>qIj|gxlOrF6jpZE&`6-`qpX*y>~Q}wD(b; z9j&yM#I3x$v-7)fe-8AKrH9&##2NOk9I$RHO`g_O+xGteET_syAh+XE1+=*R+}DBI z_?b@8^sO8e7KC$J=6u50oEt4J7w0==jDR@oeQ1K$PztYerhr+G5jZ#=^{40CTdB6S zINUjM;j_o3SGUxnk8{athXd~ug5N<(%2vCvE=u=Ep?707#n#BQ*it;_d2#c4^{6G% ztPq8~OLG_S;R$T^9+j?jUk^)YWV*5P6z7Q)j(-}K9ZN->5({Yp^ZUOpK0h)ld1BqS zVeKUqv5h^Zfe|suW=b#{pUA*J;ZY`urmC@9EJwQ}M<3o5(p}wJm`=!+OpGev{Kwa& zGt9V>*wK^v&)MeIz~q2%T;-_4Jz;Vli9ai zgD*i{CC;C7B%5v~lNiCl0Z-E%Dx}tM!U6j#NQ&ov_wDOh#*{g=MJc`RbMdu>rO|C7 zc_FxX%xxqRvbN5I1L^HkSz4&Sj_1U3YTA?nMGt8e+Y=yD!ex#&`qrE4nk}TlAq;z& z!lMMp{#5v6wY^g9dgXU<<)HxkeQBuDo3+h7q~fn_&rXZ>#?!nHqT5(_XW>z|5kTrJ zEg5o+{2_?z>P=|)bM}V#b>eReS@^r-7PY3@YLLi(&@}MPD;QP>%>J}v8+SUYM>cb_2bRxXd#M&0K zeWlB!pBG<81I8n0A)WBZISO!kdsa7v{u^F+X8h|Bc#Bq^e=h;vb{jo`&TG%FIxlmgZJwnHNh;g|wGwX-If;?4X{HA zX>WIho-v-g^VhaJ*QWSK<9X6;=a*3Moy_*3Jjt&iK>k@{!3f8v(y=}vd{4gD3{BxL z7+QUt$OP6i=`jMxdgN!Q{#CcLjA3^x`G3Q+A8ArjSB<>SC!68*z>*`UY1h(7>^!KY zDjreXuRMx|ABE4SYIC*EgD>Njas$aVqcXR*%6LBY>6hLd@#d)Z`rd*C)!7-hwzPPX zCx{Q3Rete3@GD2anrDhW9qJP5o+z=@E`-y9wsxALN%nm71#|1$wPEa3q?Ds=ceaZ| zPj5b5AL4o8vhgm7q042eX_xlEkQUlYBHn8ImdRc#KIiQkmX|am!P7LQl-t}}#^vAx z_(su!KDF(BCit&((aU$QX&SUJ!8t{m3DLx)@KA;v{uRvX9~eFx_+rRSbD(SXwwVhR zq@{9yyxUXe12t7?LNVoYR`VSx$?MCnk>o$v*R#(YuisQb!rIIXV^ zYS1s6V&4t#Vp8L3J@LvYJvwLc>t2`PUyPp$_2K^j2>yW`*}IzQ7fq5hXy@}-9IF%1 zW7e}gVSVG>E@w9$6xOtemPS{XQ;`{_QO5Ago-^K|LNH0GDKEUIUUe5Oto>|a+}-#u zRJm1y!uPsH&{paw&zvMb-a_8pjb=;Yty0z%xYM+KFk8VXNG${aOzWN*o2KvOT1QvC z(d^e$y3sX@$eH$_X}(f%!*R(Y(w_eS7W@yUq0+T4gch2d&zEC<8YfKpuU~quc{uyh zQ8VRhTQ~Fn05SHT6mRtF!3?$+a5&~;6wy8qjBOjbQ5Jy~zhOSV;j5eY_Xhh+^2U=# z*MYT8e+sv!{6z46kRI~i;VzeHAD6f@X-{&$pJDG%!Q(wwREm3@AK^uukjp3wBycn! zBX=v2k@?p0ojI#b-_rj8W##?1iPNgkE(t3tQ)(_zT9wt+;yBor6PVIZ_DAcm5cLX=y z^v!n`{tnYLjV2r46L=otG;JsZU z;E6Nk%_9x22OMU)dv6jvx-HB)6{nRY)A?}a97{5epmyf3ctgfXp;#R*UkYhTSnTqy z>|~Nmo}>fN{VHclGuN2WN>PilFtqEfO7CJmo(x&q%O=psK8LMUy_Z|CnEwE0N2f!R z#FD=xXV80Bd7|lB1V~#`@%Epl&Sk*1x$yG>!1l`;xvE3pJ2r&so-y*uzxvp;$N)f3 zrzEX!8gg{?f02WljI6&?$z|0wl)HOJ@pY27m_SQgW-55kUbVHS>eqUjz`Fkcjc=5s zc~o{R6#6brcGtSs!W;K^G=CEtdxy6oad)8~B4+;ZE6UaX00wwF_G(z#PZR5=`sk{V z@j)zcAs=3bvQfm!R*aY5p`@!rmhFBTo?WeY&g5I0DHl+2&i?=|V~GZO;0|cerk>77 z?`+dkW^P2DaaN0t`L9UvC&TXtNqZ&Ek7akNK+~VG+i6G<rfDfsnP^ky+F~#$%BcInr&ZbaYKqecrBivmvBR|~>R*-4% zg^FEGqe!q@bGZlD=aW`%{C$0AXu2f!P+X$QLQ07kV4(YV`qXiJUrjE5utTj~MGu$) zWg&fpjzBoc`kKvFq@{NuwLV)|issVB^;}t90vjZ}QOVHv;C@vNv9q+>4TZ(@pzy35 zkVyL0o!5?aTPHe%8pNtq^6a-uyB?%t1p8JW_Vu2rYk8jwYz5f8Oyl{k?dL561yb-VX#hP6{{VnE42yE_dcF89h+@Ov|DlZgxa_>W%%4^%J z)lD?pj5X zMqY3N3wCBGh_!<*NpJn+W1#cUp30aWt^%a_aJkMx1;Il70LTWquS1l z#f{C-=eZyrNTLf^Y%W^jZxPKJj9{ua%6(3H4|?5;!&)G^wbwjeZ#9T#wq&%EMwiW& z6W@OyDEib!oZGaW%l8(D!M@bA+g459BN8mFy4^4tH+RN5d(&=bu(*w{#k0*0;IcKm z;S)W^393FK9uzvX%WL4hTH4G2Ev_zZLY97$&LD}l#))#W7H?jF zel;}@Dt`5n9b>NE-dzhguNQl;1kV=H^vyF-)hy(V?Jl9X7ZFZXu5DPHpTmMHGeEg( z*#!D$gf3)_kh@D6G6g=Z*m~DPVW@a|?#=wm>zLch$C(LH8o#ax>M6qMiO1R0 zy^7ZRwKx(rTdb!NHNeN1Kc!D?qxo{OZz;d7)fp$yR&D6eVbkT)lgBqrcJ3LAxK&oi zBN*e3K&qA=AMoYIp3vXwxAWYYe|37ZupYp3jQiGdsQH(>f0`VjeN(a0{{UxC9l|yK zq|pe@5)=TI`s2N0_{R2s2|TzI|&p$#u^UTqx5uxftY;G_q%F z`X67dJZkPhQYExcEvYMX8J`GdTTfZO&39*5GiTf+Kk1s);rvOp2I0x~jt(%pD}!f6`+ z0K|=^8$-gaaAVKYI0RCsQxcZ8x6B+UKIVn(jgnmX*U%)u@`@w}=JYEswCjI#`r+=`a^E2wbV zY(HrPZjn5|06xa5zKgEv3aur9S60kx1<1(%02;V^IJVX9RHZrS**(vMt&1jHOmUdo zaTxrKQMvHpv6JmGJgj>Jk%1q9;gGv4MjJ|MQX3@=%taNj6ghR46HNIY++ykZHocf8#uKhzEfsXml6 zz5{!vdGvd!N#xHX24BE~(x*Nm@Xeb9wb5=JSNM^a3+yWwDv?^vBKc#?+KA6T!D zR)2u!Mi;u<%-sI~gvLdNOQ>~1{{SXg7XX3uJ?e@0gRRdZn^Q6?V1VFf6-M8}z9_uD z6Ww`f61mzW9OJfe*0x~NF6|}q-r^G$%mfhp!20H?RI4o&(Tu&6A92>cB=Hp3w`-!O zISzK5dsUI*-D^pROQ^%<=bh0IHv62_Nc7v-1iZ{K=FP~-R%6|N2 zG6sJd)l_g=vK(P3=!s|X{o`ZHXwT2i*9XQDC1ykS+E{a+Z04!D(TNeYSo8_A=CF=Y>l}YQ2Z~s z9+hrgUU=dv+I%u78A$Q86ZNMjQLSBsxp}Q?nK#;{(TQiDO-<@BL+;vsnCVumFKw=q z&Aij4+)oOwTo3S}yj5oLy4&fIhD@ukmP(WD>rl(%)1_K8+J%+1ylOB|Cqej9y1&F* zQahEx5V*ErpY1fFLn@fDp) zw57J*qxN-D+5N{u{hMiP2G+Em)N_`|V88ylo;@X=HQRcuG09}>SoXTFhh&8Pjq*Uw z*O-%!N?Em=q5g|xill?(N@ii7r=?nOR@6CW)r~oHn97iB;+04t^Cm`pvs4b1q{iR4 z(-dwP+MILk=~eCZuM;$=jc068##>{P%}o03{BwCPq$qr4QGQd8Z(1ElNm!W1qT6HQ zzxX%+LZ9$V4;f=fo);cW{ca`2PUL0G-`2;ypsWzB}tk ze&AlToi{{{V!HqkzX58RN09 z$MMx8PajuLwEqAz_6!{h;VHi*`5mRz#+x*GS*MHvozM(1_Nb@SZe&%4-beDpS={qr zlk~1SPZKv^F|*Z9Y6tN#Rw`MKs5u87)x_&Zc)J~I4|BI|K*rUeh9A9 z{yz2=`^uQxj@@~zCh-7hhQjD`_nxay;v(#1L!X;!lC?6c%?r%9(Q8sW-wfIzdE&f4 zyK>AIoO{*p4C|5jYBLG3NtAGAT<*ucXHBfTAG(+T^N-iwqmx)-3d@Y+1Rj*AW9Nmd z&rfqom~U@Q!dj+wi{raCkX%cm!6EEon5!SHbXq@)>@N{Y+szp3^RD0r*1Uc7SDC&@ z)ZpN@I#kx4AicMB7B{jf2eXl%t$h|BiQ~=7QXhBq`kpO*AG&Uwto+Yw8il-(7rk$j{{V(Mi2T1Xm93Qe5(X)vBvH9+0b+1UAmsl5g=1M= z^1fxH_T+cvfd|&E+3FKK!Z@!YIaBhmWo|305Td9_rO}>kS3K8*p#5!l}IjDSB{{V!t3uEG~dL0hR5iidvk;!yH{3@{mbKeHOh1ZTe zJ9tU|0GA1W^b=p4-|$dR3`?p10KqGNXRnNx-Zh5SeQ(5(+}(J3;zAl_dr6{4jZarV zca#0qYWy?kK{`}u=9ODq-rETUrSy`n-`~o$Ka=uI9bCr&i=imDNkvLp+f{WN_uc9D z>2u~iXX0cw8WflDTnTOp?13})vT!S(v%QbQv0J>iY%Xpa&AOUHmIJvzQ@|dT&-gn? ze-d9mh_xjNr&@yN9S+_Ck8{-3eN$F#OHsJirkEgHw$d}6$fM{#TK@o0o_(Pz6*?;gvUx(ik{srnQs(7#AzK;w(DY1+fxtVRo+nZ?*d86fl zJxcl-`E2(*!&0q6SdA&lT(a^~>iaD${{Y3Q^;v%j;xQQ|YS=WLdNPFK?(bzQyIZQa zGV@mIeUo#n$KopuH(!QDbiUG|of!uNHc$1hQ20&pip#;i3{MQ*i4ZmX(=@>RpcXwq z=Dtt;hkhon#c$bT!X6;|c)EK{J+%3!4V}*ftcUqmu^*bvBUvzV{{X|+<~9CV@Vz>@ zN78dX!>!>e@%yHc_xuZ5hHc_4YVT>rq`vnSeG}mS0Ek`;@wC%h+g`)=%!iO%-T85z z!yAu(YU}UxuMI`EOTP=P#K;2)5kr9I(34*o+}sF`=@1>GpO=%5O3>53Ab6unp4U*c z*KSF+a$<&Q*@wPYJuCW(Ig}ofTA$E$GN#?(Ejxd~KBK+xbNH^}fA~yvoeKKZkc${@ zqF*-4kGNQHeQS~NC+zd^r0Ek%Ej)$Cnt!vTAgg;Z=(VNrFUEKO028!1F7GtESmw4Q z+}}qySk>{ul=a1K>d;?9vON=9wvtzHE+Uo#j1$OC$3B&{dQ_^@QgPG zQnkBmcn-axd=>a_aN2#=g>|W1E+;l{TC~Mgx;Z^P$Q8i&pTs^2xVXO5;qX$WpvL@M z2-t!B)i@ygSGV~74+{pn&~?8R!EdTG#9f;!VH~k9Ra5DJbMIe2d_?#yqv?992=t9x z#FylmZLh7P41gZw1CE`sUm=@R#^K?55msKBer)=jzv|Ur?_}G4y7}}c()@SubHuuB zw5j2JQ%#a-Fw$Di&ao)qbJL#n+xR!({{X@dh&J9%g>mAjZV-_i>HFB6gUaOfKGpMt zz5(&wmCfF%7O}0`!qbP227wpKao7gvJ&#)SpNBfn?F6Z(=)M)alw34MYxlTijc`xi z*qshNI@dN=o?>cLwkDf;X=ShA{{Ta$8YPVVJD{I2Q`mzcYg!@janoT$%A(zuoE8JRA-9qH8=5J#XUi1(|iY`yw<>jKH!W2 zh|DycM%z6jYs=nj2u>$optcnQMj7w%G%?@ma;|Vq?Zu2)T<#scoJ}< z6`|oD7RtekU9Cmhbroi^raIt)j;q#n&*8R=*jC6QTq;0HqFEU(M&!`yoM%{Oq z$5KbFRAQ$T_gDDQq<@Ne$yh3<{TfQZji9C(GC9YRh=s}0sHrKpqpu^|IzBtv(ztaP~Qqr}LhIf}l>mLvTv_vR2`eA6u$0t6uv*Vu) z_!int-XFA=R9RaRUtU}QVa^LAeV7iM)(40@9ie!SR*u)gNv5Fkfopqm#$Eu-r#}pg8WMQtW=*Y-{5-wx8W~@+Jy2!;eQ-y&mf0>)RE+OZsY(k z2enpsRt8!~qZ@wm6TaWE;h<4CJbtY}%nTFysj0IkH=ee$kP7mz0^8Wxco~1V#D@i+l zT@8is*1N0Ni8TKJg}0W=YMGjMCv$t`oMY)+jpf+52Hwskj!_}ZcL{(8PpxhEgGKm( zqcFLX#(r|a2F6%*Qd?>5upC!8_Fsq=o?J6A4D#YBZsORadNViktgBT~-9N+du<6F{ zMu-lPtjQ@dwW}_EaEy3f{AZe$>c>fn-Y6rpStN2pCj?+}D`v|}@iw1r)~AoLW^3|yfW}U;0?UDfz*19z>|+pS}dC1Pdkmx^4Wd|GIf1& zTT^D<8WF|_D1q^xa%pV!jcJ%kCXpN8#7~Bo%&9lSM|oWV4-3l=o4cV+-G2(7Z$cJa>-zZ`x>KBPp|ADkOZ}i) z4=QLpw_tJQjGCg?4rfT>wz(`1CvVG-Q&KLs6L^8FZ)`6#$hpi4yP6<6ee4oXdb29& zS30$&ynZIVU$lR#@?&kP52}u7sl#<5B?nE~Xb~*ebhGax8OyhNN4e1az`joG3_f@Q zubnP{+&D1b-N3_CTO9!x3iVx$cS4`A9vwYl2PVlr1_Fa$hncA zQE_Q!1f-FKRp%YfPfCvaQ`BahBr&8hg~<`YxVAmdN+j_;rk^p6`Y7Ov7uv=|+mq>^ zO24J*zuGJwC|*P-3PfXR_5^jQl%`JyH6^Ti#8ZKB zlZ4G~|+2av@aa)0Aj! zOsAA>e|fhkp|Bgs`TqdhHs3OQ`{HKaKWG{1`o=Q!@XIDN4S+^x704>5VCT+oRj9ti$ENaPhF*8}R8-F0J6GE=`ozt8RfZ#wBSRKBLqcz2VcKUr!o+ zxsnN`*q1j+u?M+6)vYgkM#ymF*379L(+4YUY=tl|ZY0R}>(;DAs$5SZNA?s7jf4t* zT>cf)>pDNf-7~QrKx_TP2Y(-IRm#4|?Z3L#X&SOu2acC8*t9uH3uaS{TN2 z&<{%4zD?OMlc~{+n&#nw4ux4;fLj|!uf0(FBv9S7_j=Uq&yAu<32gh1N}fB-ChY^X zBusk7**j0Ky+a%x9fs|n$d+eplOc1B!>`h&q~mq^l@}{n>|8p%sxW(-l~Mdb5agcN z6>7#iS)*AMuL&+PCGI^j=~$PZEP}z2?eukX$lM3Y2h`NMFNrmsM=5t47NxVW33G+* z$>~!H^pxz_ohe%TT-TcNaX5{1LL=OE%Q#g7nu_;CyuG_vY_39*`5$LohW`L${{W3M zPS!3ahF!AHdLd2HtT6Ey^(K}t65FTlV1`GPeqtT5kH)ln#<^MYBF1Bmv?vZsRboy zJgl63))iXP(e08)mNi$9?sZuU75ZbTu7)F_#$E5^SP>T?qkzas9S2%vz3z{wHIyY(y{wca>MVs9DbEr=fT&y(yLtSdrLpe%DLZx@A*`vSthcS@}_a%}%I(p-d zDNh@iR9v!*QZ!h61vkwZHhl%)-5FV_Wm8zbbSKj!rE55soUCJ&F4)Ow9g-y z*vBk6$F^&fviM)BTi#Cg`tGErZOxUslWKO)*0`!x#NptTILc40<)@;({{TZsP@^{7 z;O5X_|k9{7vBrZ*F6ZK^CEB0<4y>kG&`)W=UA%lhYNqY4KCw z-Sl_b&x)@vPs+RzWs^zt!ZMV8h5EB_uATZCUjouecew*f& zt3HdM_+}N=r_ygN@2}@Oe$}YWla6;b0M9*hQRyEKuQb=XvA6NsT52}ym?47Ei*4vh zag2IqwR}zBZ;1M)mt}3@FA!{ z{5{irH{scI?PuY?$t=jzYjDdan5+*9yNBI9$l|%%pMthNCDpDk?55S4*#_t%wUj!A z1CU2t=e0!f*LLMi`u_mHGmah$TiJRZx8aQ|K-A?%*5Qa<%!U5Vr^yq^CJ){@=s@(S zd|7#Epxm{i{8-fNl~s}}eN3YO4?B4Vyq8YYJZa%;rn2yv@)80TGGbPcan5`FYN7D= zi8brRk663Czq+{24c6BxbJ>qURPm}X<%AvntKTgw(&|2GSS0+92`H{?<&!$Pned|v^@o&THXkzfho*G>lWKd&~bd|W?eO1Uf zHNbc-$4JmTQ~v-740d*xZxmZn(^igImNh-fjz1do!|@u+;i|^IAMnMmb*R{rnvF}!8*Bf{Ppwp%S4YdgmUnsKS<;u&I3Kv(Y% z!m@lJ;$IDTr%@V`_{T}pC%HzA+UDDs2daX8_r5C!#9IFV{3hD<&x$X+BV{|>Wu!p} zk7ymp&j;yM>^wiGL1`|zCxapK;*gI&%L8FPIV2pA7ax$TJ|s(j7-?Whb)s1$R~zmlhS6i4ho(UJ zuxoCdy;3o8jrX#-b5i}F{7-x?Z4JG;T1k(yt@fK6yLxUtjd6oW&^1j;d+m2tiqYqN z%_`WL4&m8GaJ4kP4$|jV^KEQypeLD`*fWn%wP*Vp+RI7vEwsxRDDRpI@b5-1uL?+Fj`ySBH$HhSa$GK0a@u?de;V zv06!M{{SD46Op;FjmY-&rH0o-k$-Y!oQwhjwsTuc4@Op2M5?ocz1g2>Wq+dEHLN;% zH1CE55q7Hkj>p=xCDuGb1XAhxWC^tZ3lb0s9m(}H*j1}H@+zmtKC5s#yo5}Bzhc=TG>w!c!N%W zvA%fYm2m423OVd?)K@#9>$-EtBkJ?qL2`|YXr2}U+qiBzeJcY~ZGXhNqQz?REG$@U zjzU5l^yjr}?Plr&?H>xT02u)+NlZ{s-$+;<3^+%|VjtaVcSlij4L5 zH0Wi}EUy5AMrh}k<(A?*P{D^zr<&!JTGLidBWS`gmoiO>{{Rb9#&Brn{q40&Wzi!*TdV}bslaDfAH+ejJim|77 zu6S-*+WS);Sd)5N$f~0_{v4ibsBeebwwE{Azu^jwH6w{4G03EU3CM1rtz>I{6VdN+ z9YLb^PkpEJPLWugqT7gtTa-ZQ=`UV^X-&uJr+Jj0ogwY^Xm_NUh8B zujw|iHNT0hfn@?o+A<&NxHU$`_rjV>Noj3i3{AH!9iZL#^U}3#rP7e@dF>8JaFRoU zKVG$@6-hg{uaTVEzJ?B+s`#EgX6EZx*6gp~ZejAIaUu2Vn%2L*(slbW1ou+JdMtNK zxk&Uz916EQ>uWLdbo*uAGNw5AEB-XEbq3!po+#9yWk-Y&x9M8O6SIqz$dgZ0jJT{c z+i($Mvy}_s1jHWRs%emEnstrpLvg3X1IjXG*NAxc-&uc!?oD8JGk`444{I0MvTnH}Z4FtXa~HwdI) z8SUF}9ghR8X+zi%18X3Ru18GzX0@Ju7fMkSz9lls zKq|?#uz%Xf%{DvT4^UQyeRN73eCfC;#(xY{MmBrDOaB1Fmn-hq;8w7+(lo?LpF+2V zAp>F;Tn*okOw~O?FAqa_^Xgs!op9b_+(M<}VT|YHHLg?P{{WW$WN?I!i5^xVKpp9} z(s)(~Bf8V1fuM1>a$X&SpRW~sw^mjonJ2R-!{R*^T`kXtt>j5mY|L9o&(^kuzK?B* zVbJZ@kC^$3#(Q&B?@xoSu(#T!mL;GU%V)%DYDqqReM zB&_JaGcHHxRIj{QXQne-9WLha*MP9cf=|@bpw*70ZlttC=l7XXHm_h;v7?%r(&AC5 z{w1MO>hi+fniycea>N3rLQs2;YFIR@ElMVm2rd!X2{^f6;OCLorCgqC2N;E?1Oh_H zjkhD|*wZb|tP1}CW3UcfgD8oJWB8g$b33+FuH&H{o|&e?rav$vh1h)J#P#*5qOsCr znS@%Es+Hxj^JD9pp?$6EkZK`x%UI3^@AQBda`zsUXG+yp_`Ho65}YYnQZR?q1MOO= zM*O7v{X*i?RV>eMuiL7R^+d;veSK<3^sf<|UY`<1$`2u87#wrYr88TXO1cecG{iRF znPWi8M`P_tYkOs72b~Pq!kzJ-dzE(!2bLn%7eZ(_KttUc5ec{sW~J zS3DT!(I>#eDQtyd_?l_)UYTR%-AQp3zVI*{cl18>qh;eQD_4*R^;=n#@Ic{|`W)3K z3vOij)72UGlZaztx_$(Ng353J_pWbK(|ln!$!R3785oVDxBcm6|G)oMm+rl}#hX3wIN0a9qk$?-rN=)4n>@+uKub z5{afoY;IUmE#IH5ZhfZ3nPGx)a>_H0H=y-3JW=VIuHPy>$9{Lf2qTYr!`Q=Xn@+5p zbyhhk{57NAh13&8-y}`TK%g9Bw-o54(u}@bx^%9+*5SYl^~Xx;Vv5sEiOg+!Rh#9E z#10RBX_pabsVO(8^DCxXbUXo795PFw{j6=Vffc5Ubd4mo@!JH(9VP`$B-&KsadT;| zLjVharFSVl{V;R~qBuTl& zRD)lIBP)I3_ui)r5+YBxL&zeMr5ozs-ox6r8>RUf%ckE-f-7xO@tB^A3aR6*X+fae z$8Y9BCYy}0xR8QPT+?;EI`ZE(x@zDY5Xw}iJ@Z$5yA;OLEPR}*l6b9sl9sH+)t&6o z@W1>V1h>%t0KqT3XC2L?C>vbSPs&Ln{XQ=B{PfrKP5%G~2+aiF@JnABg^|N1xuSf# zNC*8sD*kqB*TJT!>UkL1b5{8u|Iqb+*gU8FGyM6x3;zHY7sg|5-1YG$qh83e0i4a9 zt-;yWyi4{Mkq^LIlp`!YBKXJwoPW&8sPkUNZjn$3qAHl~e>(g>GpM7PSN{MUzczm5 zmealtm-tuYO4Gnl=1F7({NN0Jbh2HnUkW?2v3Q=IYhaY-W<$_|6D>zear zDV>f!@bM!vg448&PE{Y!zzbcw7I_`g#I2@2c+L4|p<*7X) zwI$9G*r^l-V4whZa6;#=sjiPf@D8b|NS89e1R3PF1d48z;XQCTk}Wnua2SHP#drDx zY9c7@?{uRY0fIyHucXQ{xYT^rlU_%WnB(O}7N*tN9-E};T0~@7TOe$F-!EFf1>{OW zW!O6$XD8C8lGeus_PJkO-3><_wYQtPDcczPwe|Grs(K}3fe~4AMVUbs2WtA7dYO1qpjF*!+Ytp9R++Rj<4W=%$ zZ~c!a3wXb0Z(!$d^{>re_&8hRMwfT-tKlE~6T{)hamxg?nk1BMcA+ z9RX7_FzRqS6Q9z^Z<|c=^&~hp%qCNe@~3eARrn$CQ{qpGKWGozd&l1zd{g4PeOFM_ zJVU55m~2c&Ew>GC8owlEVuc47924HZ3;GV_%QMF25qm;(B>hsUZ^M}WRN?H$`#U1X z&2H3n^R(^LyHBRYuMy7%f#JcTLSsjp0fMr!3%$HyKmU1_LbDW6MRbXLw~}y@usQ$pRd`^ z%vN_#wkU4~cvWCTat@i;+vKT_NMn=jXcv$6fHlc{UHD_9_!r{lui?wx zW?`paY`beHMkSIH5Jw#S;20jC-WBrMhFylOU)ka!-8zZE{OtPd)vljQA8*5)O0d}2 zs85z$t@7IUNyh2?pXaJSpf87ivwoHPD<-}0r^7fpD|o9>)wCy4HnpAP?Q)xO#?WMC z80@0Eoj%{q)58D>+~8z?v}?jYV4olO+sB`>M}lMchvIvUV^+{~h0$lfyOc>Ri*Cdw z_Z&NIBPZ6q?@K#$#AP2hnEwFBgZGVpmwYQvlj%F3LR05zScK)>sjYrz)^H-HPsB>< zinMK3*XXW{>#aH|0hTxv5!m)WN@kCtTRx;>PY(Y8XgjVvrI@21TpxP7I1#>7Z9B3F zY}8gdlUQmI-P@?$6oJEI9S=(Wp>&}qc7H>{wMgGn=dKU(%549j`(6T?YyX$;nO*Gai&A1{~3R2e_5dMYrf3oUKiPxCy= zs-+pZy)WNW&AvHkUNpJ2`%i-PZ82uKbM~uy`#tU(9EF$bIJ{OJn_d4N7;pt!$8k&{dqj#q* zt*Y*KFRuJ9)Y)6cFAl24Ld?siGARUdSRcGs~BJO&z{OH`>cOTBb3y0E6jw#QYG%)vFeVp(2`&09Yc)P`ui;Gn3`f|(v z0FyeaAB>(d@Jlw6;A?*l{f=fOX5{c9(49_RC*yPmFpI;njn{6U>QNk)ylp;3NSP`~0WaXObWr$+w(;D4-+!@xJ6 z3A7D2Z;hT^a_NrwB^qdj%MPC~9MqmA)jj}tf-f!~0cuuE&$>xty(UA)W7vCF7rno{ ze>yVWr}M3!(rb?#Ib94p^`|@k0O`{|_=@~f&CP0m_zb@;Vx69yrFeT+lJ`l{uI}P@ zI}+nmbtt~41LibpuxT2E@=BfuRxWe8<54G{!D`+~>qTx6TJb@j#!+!0WMmq;_)120!cE>DeCO|UEzG1Ypd#4@?77|5NX^QCue6X>T!;1LhHl&dt9W8;<%<9ZjtOD-yz|8?nY4pUSlKuMGSs@P~$4{_|FoTh++|DDAEa7-K$!@II9l z-ivvuTAA!VNfJi}c}jU|K3)#)e-Tx#JTc)bJ14um_@QLC2t)ZvZ0^PX0K3?Auj~&G z^UQA)aCR3q#>PI@v|OJp)K#S@=$mh4)wB0b70oN>JUfTQ*J(~^wxXT1lUr+Lr*oau z^_%asNqMJ9_9gR~N7@^Ex3{?Wtqn8A-Z{MTblq#jNY=70**8KY!h2_-^sQT~-wx{A zwH<53uYGe2VIa1#(?WT?dY%Pi>Ds4=9_D>E$HKx3eC-BERoTaWoeh0XvZ#}h^sdv3 zO=^yg^8WzCms)&Y7@p@)TNQkUK^e*FeevyEOW`ks65B1Tejy6ZQ+(IABn1b)&<`TG z4O%@`H(O~m#)2)`Que)jW}}Mw8#x8N&Zi@*4=tC<#?k5RT~4hjK3PRJbNd$gja>y{=6Vhe85L>#KA$P4}6dR0c7 zO|>|fND@g1``IPEk7{hNSzM4Nf>lI0%uF}`0QKvg!U^6g%(%+Xvv)ccPSY}~3rADTyOp0#Pu*fe(3wB1tL7%mAhg-~SUsZu#n?OcNCdQ|rE+<(F;9I|iP zE-oee+3E6%sc+%S;2J;eNa9|eTq;28(DPF{%}ME~l{#`)Zp-@YU(~f5Z?wJSFyG&9 zU>Wj2eD}w#DDete#DBANm$tfRKfal`1bT3Bim#_>sv{A_;!<|EOb}Dl^VYVsy)(lQ zUr#=>;;Y7iK`?nx49IcZ5yojHR&PW(N}N`jrRsC%PqEiT{{Y*UN&C#U@^_GX;2&C> zQaXGoZ(|0(f3_t8KeNaq*~hLcK{TxwQ)VXjBYprde7;%f)424irpD&kT`jc83WwZU zL??Lk!RbPxqORA>)7q&!Ut+zsk>R*hD@}JPDBm>tgB+93C%sX>@V|y%O)V~?aLi-} z_g5z%cNz4n@=mc!Bb_lDa^<7iPTzlFPPdxuptrQp1ZY+TDb=kZ?xV zaOXH4l<7vE$@bW;RwNvfdC%utMucp+bg`0~=6V_SHWGcU;yQFLic1#^M(g5e#Su5u)IX^%$r% z3#crqE3^ggDvakr;(V;RQxWi4vs#!+1<(llseVlc9}VxfEV;L`s9!Y$*= z>nnTB87(Ji8HL5T+!`evez~o`5ByBmEG3dX7V_F$C)Fyf8NanM2>9@@(H}`00rBai$V!sx1QhYY}gFL}?%ZH4+@otc|7-xac zO4{(x!(BsAXOi-1XGjshg+#Ahy%yX`K1Ps_rcG`g4m%hKx4X$@WTu@(wT zEZHEHX2PicwPNc}hT6a&J(ON3jyFfvqP1B@9V3e+m(!Y>TYWmtarVQqi?H|N)rw1Fl*<)oj(4298JMobm^~37O^p086=bQrY>W6^gq4mu5o4Zs_~4uOL4${3@09y{&3* z{{RyERE|W>42!r0^{$$gAr7}N_7djzPQ8t^XiK9q1C=A3k9w#+$t8cwt3~=GC1Sg@>FG>58!@#2qs0Y+7uvGW26B z1_R#*nknJ%w0-DVKOs(aq||Km?qlj29=$T9)z6Q$$)sL9*yQAc^y`}D7sI|b(>zma z;sn?HL3JjdDVI))d(vW!y>|5c>$IE2_xUSe_RqKED$o(PnsiYH`^eK9&1Od_%F)9v>O_P4%hTWkvf<*hrv&pPZEgEZuMg zSC{si_+{Z+OSwEX@ISkL>9*XYo)$RBeu-F~0@8A9b>{{=Pe`ndOU9{hdvBe}(03g4QXPQ2%+ZnAJ ztw+H+#)%cqm#OJ;>7o)6Xtkj!MICmj!wh;?iD^FsJPl_1ZSBOR2GWzvT&VgQ+fNU8 ze@%>A$Drx>h-F;F08#jLuDTe!!b|tGqaR5(`J+0NaH`G|lecttC}FtPUM&yC)GtD?;PH>ctx|hyS)+NZbt$dchQ*Z`uz>a%>6&G?hP4|VTu$1* znGgV?Nf2&8?@d*e?Q4f5FMIxG%t`R$S+o;c>sOj{qA!~aR>pGOvCkOIF|2NM`PkU& zP|X9d+vnSPY!6J}bgbK$F6`Dx3tPxsmkeQG6rR8@rBu1|4VAoa^S<6$Ndd|3-jw~a zd#f^39F_UBIz`jeSgKFrWR_cKUUyZRhF`C9HbAv1r@<9yW~Sv(#jC zKVB;xJoUMXFP;?JxU57m`V(4*#2RhRo-}KXCQ&4NgcmUYj>jLwN8YN1NICN-VkHSj zRjIAv&jnm-S5p0##9DToQFg4i%v~9~jE_o>SMcr5gLy5!spBmU>0pZ~Erf>+^z}87 zr|LGkc7Y$-G|R)ik>-lzZ&_JSBWm)w_U%{o?L$iOEu?M#00_sB_Ta6!vwevY9>INc z&P`NgtkU1t{s-zso0h+!t7{Zj7O}JV?@ojpHYOsZ_5K6L-uJ3@+9!u}>wIcHCO3vm zBHKYD0?nViOL31%=jE}wl2n4yOO_Z6isSEr!TGvXtvAEIA@N1TJ|A5>_Zen0-P{z8 zHc_0G$LUq=loD-fH6Jrh;%zb^6t`H4-Cd8G({3_>{3?T^A5+C;-gv&lSebOoJt7+* z(h(uJxn1}tjzI0y*GHlJBk*pfZuLJIc)lnv7A&Q;vK|N?=O33^qv8*Tz7&sAwEoW2 z)uNs?c5R_#C%NgxV>5ghU=ajc4g!4gM_ZHoiuS zU~aBkGDfh%u_~eJKqLc-!dTB{`C^;-8%BgaCek$}_=TqZh}h4iO>B{Ln8%&pTIBT) zjZt`g(XVtlUd5~Cpo3)_98{sM7=N3pEkTg6&k zwfM8vtYWn%1NZTj$;WeyV!5Y^##C2~xjj+UN`(aM*EgdyZT|pi9}wMLNj{&f!q(x7 ztQQvh%w+UA9Ot!3qx@O%Rn*(<^)|E|vhKGM#{U5AVDt2>C^c^cyfI$?0A?#p?AzYl zqecg84z(QqKk!rs6oSza#G zV`RCS(q|wPoZEF@Ty?Cm<9~@KTXeiSHPmPQOkx8O7#w`V*WR(^@s@)L52bi|;^o2= znm1z*UVVD|)?8jL*CW+rx4iJBlxl-^)VN~9(C6t}*2KmdI-hpGq0b5qN&BnkW3+?f zAH+1cjLYEo-rOheWrFV|TfPUSNjJvGH1zuxxoIo`$qF9X$p^lDs~!&&-X*7>W#N0q za97V~3=^N?R>m>uUaRnX;phA;oeXQbwy6w8a>-?9ZzOWe*zEb}eT8;m>s6byXUyMq z{{ReST9rC5=A3@#{{Rko!+68P7Z3if-avUt5f}wMw*$!fRnj#3#1{~@BskthKL>s&#}&elhA}$E50WhPDEG{R_>>c$ z!l_WH2k(ANh}5M$BL4PaTisfTg|YE7!hEn%8Ho~pVx_;+nlF+Igt~|}OtOL;4^fUq zSMd&+b);NHr)#=3?vZd4bk^5VsVF-Y!8?Jf7VvBT0K3$!^xa47u&(1eY+f_>h$p5y zRJqfN*Yz~yl2=UHgGg&DfMcALl>rY0^OYI)&&q1TcxK*tcQVMsJHLByLlgBi$(e(= z)$H#iXpGxPj&K7Wy$64Kw>G8Xi@8J=npOS6M!zt$w`su7@RkFLaj!wj&f;{cH_O!9 zyYQ})3;DL1ly;N1Cwzgo5!>8VQ=n?^a2)Eoe%$h8Y;*LkV&?w8~JMKZbD z<=gz({^`%_ie<);aciQ#h&4@qTQ!D2hRY$ILVXWK9_F&ESx4boc{2Tk(pQ(@ck@}s zkQ-~i5-V~Bz`*Su!}`@%gGN+uJH-)5TY?OOARkKe`E@7MBU^iYR_|(HiCRe48ST5R zY3ZNYM&V)9{6{**=kBsb?ARR?;~!pVc)xWxuf&;9aq4z9e`E5@aoxtzMz|6+<}>Y{ z#;C=pc#h6ng`dODZ9{(U=le`ck?Xe{Rz{r*zY2?~0 z1bM$J7|JmR_)S$hj-%1r?lN(V7jgDpDYl<{cI;no8>Nu0K7djzx@ElLHquBkHsbb~ z+7G^Z=AZqE;znet)EwufTEo*y{I(LUB>mQL$>7aT zOMz|ly;9@KTsHqeCf9GKb{X9io3MR_dQFdme16b9+@21+nld>1 zF4jL>6PlvhUaE}~-sz8LGnNpbP%=kfV~+Jx!=-BpRj9OfX9KJFN5r=qWnUKRQAwTA zm=1rXWZC#@##%;6ju7PyF8r)i# zQ^Z$D!~R-5n*RVw+Fe6Ndtoi@m<&(@46sv}rXxOFmle5+oVqcfhWe zLE)b{WP27?ZUGYhjn5TnCh+nxm&-*&U!BP$RYr~5ZSA(CO0<*JY2=PYuJk2`?N{dF zR>sB&Xv#?U6$YYoaFN{U=5qV?yhbb){109$wi0-Gt;<`aZ;_F;k0a|!V$fxAD?osr zes1Qg*vqAf`pbK!aYow5Q6FX(`G-F;No#}o`&GMJO*YsvNoj27Xj3F(l6_5dlO}*j zB_dWW+5YWUL*YoIDQY4EKQ`t((v>?qqSaR=YZ$U?QQI~xQbQW>PnJUcGg3`0#m?e1 zYj@kqhm7P3wSOmsBDf7Ts=ncX#zw&R>xxhKO1wQ_o07s-jTCJ$HsVHoGgh6ic1mtf zM9N0@Q%8pCZw**Pr-qRol&`1fT6S8Nq|*pt)1byTKH0Lv@~U@UHq%kI!KBG4eEh6> z9=OFuJbPgSEVg0o| z;0%wU$*i6xigrU(YBizW{{Y0k+T6zT<-df;!=J;_sm*y|1VhWZj4}JnP~(c>r`9}8 zbXsX6oqySm9zK;aH;Kvyo-2k<0`5VP>55+6a@`@ujIFCW)2-XxXt8Dv0npE6R4#0u$ik4pw-MHMhgDZibGj9j2LakC;BAqy` zPm(|2;C)U@-}oi(jKGls{{U_1n~_^T^8Wx8e>}DQ0RI5Nd~{i?{{Y~c9y7R-Oo6Uw z!9b95KHVQUujl%`d<$-izjMdOB&#n&|Izf%*lyjW_zQCJk_q^U<1wF4_^CA(8=e$_f(fhBYNq{KPHA*z)2n zw*`iOA3^U~+EkERYA{}2_@eq{1O`cmC-SWgQ(IfR(=Dz0`Ct#07QsJLUrU3^^2o(a z&zs9b#l>SY1uj^_=c(0co)_?*nf|&Y-`TqV0Fhd-N1{NZ$}y%#K%=dBh1ZQG(u>-~ zrt0?v25^k5G1~^S55;@KCzjq3@elx?`C_LhwmGkPRLf~aC^hmtT9onCTjsg#B70cW zgMK=9PPH5AI&i=`q7WCBEBa!*Qt#sJ)K=08j>&3$me=+oF|u&OI9$iy-jBCgf%2%% zIoP=3zJ2f??JITS7=gUg%xvIoa=^xO?hiHSc0UoLY%HQOP6D#G2irB$s?(HinKf{= zDa)2f^d-Kwz>YXZ2OysHWp5&8Ed*X(RBkFh@%OGa!^QA8A>&MvGl%I~F!-9$u^chB z2>=}al@;1_C2rrL%UT}JoUhpCb?F`-4&F-{8k)3%H+@;EziFg{>7HNKw=k^Ar$9K^v>{TmB03qI@Ozd;3H$ zg}xblJ+jf>{{T#jRM%v%h(?!Jk}*LkQNVqOLG>7~#(sf#hf6liDe_;N**o8ESk=)QJ`SxZRF$Hg{{SOO z+8CzSY*lR#G`fwtWVa_WD6bm-0D_HPJYTg1?}~gkbd9Fyh6^;0&BDck8fM%(6Sw~P zHRuP$KV-d!R*vs=%o2$f;B*9Z;<}&Nw%fy3KeT^={9)qn58LWmaPh^b_Km&L2@>I? zW>93$`)sYxa4Y8cmW^C)0lFU)J>$vWy^@K4VPcz}3)wBxGZ>Q}6VSS`Vl@>BMO3;_!#-yiQ+*ZOHy)5Eil9rli= zJB?D+w4FCYk{vHc)1b7qir@D(_OS@#MJ~qyM*^m`g^iDx2pBJdf8RC!QE;v?`58h^sLz-zbaT=4y53( z=~ix(U`V082;6a0CtIEVO3Rk?9^LEui`qM0$MhnFf=kU#+CZ@RWVQ%>#ADjDG=GX8 z5cDYgjVDyRwvV5dJFl1U2C(j@o-DJmAo8RhO;eis`2hXNkc|AwdVOkry;$m`(SFJ` zG-)%~ya)S2{{UzBZsSGqNA|{}adeEHWYR`sh@JsV^v-MK{{Y%c_T%tJ?A7sm!CwQl zuK;+5UDv#QrD|6m7q!q}NgD3r)5|8;48S9}lOfoIP>s2V73r3`mC{_sl0rx`ry+Xs zE9O7i+v2aozl8q)@Khh!=TrERe+8wkr{K>OY1UVlTt@}V$zbTr$AlP-#^ZsK2YUW? z@wWwq&-jZhp^BpGs8GhuLM_G0ROv=>)obxZUfZjq@h=qc6la=X<0x}3O7gOHyNqJi z-e~kMhCjDY_WuCJJul!_#{U4>-^M=+HU9t+TgfMdQ^j`{R}gD07%eO@O=mo)B~=By zwkpLzAP}vMUyfh4J*UGDiI98}_-F82#Xl1KW^~omwI2`oNZUqq%O+x}J;e7?#SDAd zSy2mbeXLXvj7O6G+rBvPx5aRt`H(QYhYgIKYK zH;PMPE#LQLpTiN<7uM9B)yj zChr#cly>~4hFOBF<63d#y*EBjd8?`N^t`QYbk|3!N7Wwzzib~4{Aqs`cwbrgb+7m{ z#$F46PvRd0_>@KV$RKoPhGdc#c%_O!tOSV6yMrkqm4M@aXa4~BD7B~UOQiTy#a{ux zX73tn`VPGeXIb$KbJ@dbpiJ4_1kEI>(>i14@)kg+gUv6Ut*?Rhd+-;8!^W4!h#d?S0{ogFgwpIphBT8F&N4 zU)$a-nj23J{6Dm}Np*Y6`4qp{!mD{T})PYTEw*g*8jdd;b6r-`-%f(o1U2vOyz|#Q8Et42)gX$vN|%5PWjJ_?P3~ z*%$Wq_`9Mnhx{?B>6ez8x1S+4nm(zhTgGj!o!bEswo3v?z!Gkie~j0yhT$F?&E6iq z3l09xl&-H7}D56m-Kb!D1XIP9EaF^C`I~IVij7?=_=` z=VRTxG5dSXWAPKi{{RlZXph;S#=i<>@on+2@V=$ui<=vpO+xe|N%n6x%6XO-D2zv$ zv^Fp@c&&fhPxj^TPwdz6gTfyKHJ^vxBiFV603B%>jn{@O^av7GytuUT{{Yc2%%L5` znGWS+P^WV*73UxDQtyZ!1^A2b`@sJI0zM!3hQnR)hlzeC_gBc*<;IXi8jl?C0YTh5G*hh^(f#@VfY>>c-+7Si{V* zMrMJY+?x+AP}vN<3|0Uj<5SlYX1PUr~E3v6F@Yb0u59C64~srrQG?9%l3HW z?<(!d9)wqte%ebA{jtAgr`VzY0O4b5U>^AX!@uIa+Q;Kx!ktIr#pl4wA0BD)_`6Tj zQ&7Cn5^!!JwR44(ulGsgHQ{GC46EvNG1yECO1>IXag3(pN>QyUe$hA0E?YrePEB)5 zeUZtDqlflTt4^242HH)v7N+CP)!R**-JV_l00k%b-Q(|pAF{5e;y;Djw0dU0tZN!} zfu-v*s

    F-`z)SF7~h?ystBU4+k9AkbHXmlcfEVe`al8_O19u@$cd!zB}+vgZ;mM zsCa|M_jk5h_mrYri+Qakb8Qk%11@5XJh0y;+yP%y{D1gM@Vn!m!VP=jSH^o7XYkgu zV$f=$dyga}XZXaK1~wf-0th|p;eXq6;HQN=JNrIg`~kE5xPBn~PS(6{Xgojg>*2h* z^^A%3*;!I6i4BwqaV@>FW#lruuavCL9D!e7;f^YXCl7_osMU0(^@l3!**V6w7NEIh zB%0-dRUD&H#tBKz_uJCQX9|(0LK5YC_n*6dXz8PSStTo}Sy|lm&)PHMw}`)J?}}do zzh?gc40!8P@Xv<+Cg>NQB=J6@qFyzv+I!d`|HV$A+@n^&f&jssV0Z3cI9x&3iNNuk)+mUWzlgnvAjzGxH3Ocfo zdUQ4O#n-{l4SZ1jyS^0oAH(0YSBriyYd;RPd%qBP-^BXfr*9>$hh%KtD{CE=QAcjK zGS4bXxlb_19GPrkPpgn(a(u%scx*ja3?(W!uWd=vZZm(ql{&7axh}6tF>;ceXB8MH zbZ>&58nq5cJ5!7*b4Qlxzj?-2+pJP;-87QuGxmdhbH>&mwEqC@FRT1e@ivd*PlBEx z7Cr<1%<(PdwS|lp+I5}cNi~#m2yTp$+{hcuR@|Xiu&-eFt?@@w`1|p%!k@Ffp1VGW zpm=V}Pw`L1U3ql_Xo|-TXQYYNL@M&i1hN6Rc~KN~0%a!e?Zxmz_FC}Hi>iLqzZ3iw zsOmb@3u|v_q3Ql0xwg0QE}`ZGv9zAiB}i`_reswTt97p;)qiIX4tN{K-v$02{?+hl z8oif|HO)(3@x+?0-?3`mDSJ}!Tga>e%?uDLBr6~|TUbvlfvcx4#^ib4QHI6)@tr8t zjU~wz;V5CL`%h%Kz4cGo%A{N5O08&0&8gp7HE_7RH9D7R#Ysm+qfm|cb=Q}bwYR-A z?Cj68_5DLex|YWFE5UPW@VmfmC)|<@t_Wo~Bi^grzlXJ10^Hh4iW}uVW_hm-{{Vt{ zYkGf$yc^=*4g5KZ-s`~nPsD9j&%m1_`$Q4itbk1nM3Mw?+#pW0k4}y@7TT1^>;n10!P-3qNoguu$F((UpE(Xph^Hj_ zKM=>q0yd_{Yx3vIvX-(id^Onn7iMy+OLo<3++gSz;)^Bz3V*c_AyAhW}l>N9GR}v^dNLq zKJ_xS{pVs;<+9LLhiqhBHs*fJc&5p33}bfXjX>jjl7Ask9Zy_)lvS{|c*-A|Nd|LL z+l^*O9{O=@<;Lnati8$f{VUO2b0oG=2~2$n|!v19L6?*0_%di9xq1^DS_nkWANNQr@L@z^NH zKGoAG$`MT~qo3MEHlBwd*Kviy!H(G+cjAwi|CEq@XhQIgLiilteI0JV1hHi^`~kY2Zsf$R~DBK_QNSzQQ-`M zdldAj*5=+w#G>X#RU@64Bi#Db@atX@v$fr>OQ_8wsS6E|PzThX=~pOG`)Lsw)o$wL zT|Ysw)9z&5q+7OQ+Set5w(sj!VOw})+OQ)P>Ja3C?nP_q8s~yc5Nfst@&&Y3H%iv3 zz&AucG3VS?X0xto)01)GO=kMq`Pdt<$blwLb;d{GQOiyd=0znbPWwMGXP(Y!LDPc8 z7$EMK1fO1MRIAA4ZebC+^AB*xc2qUP)p5DIM?m^gT@KQ z2U33uCcg69=9XDWT&W<1t!Gl3ft{;8nV>X#TV+;(A_M|9x#%dGOQ_>9+RqaM&(9zW zpFk?5jBrYK&8Mj{4jO5GYPl3vHxd|D+%D7I{XHrc*NPKKJ21Bw5t7#tAdoQJ>67^i zZlFBao;9`#T;#No0uR(xsGzjBjiGCjbI2s*kN&k0&!sZRCe;IEi~_>|jl53(poM|qLKFW0>-yD8j}*f@n`P9Yx7sn5fb;aJQ^ToqHf*C= zJrN>kk$L!AN0F7uCEM<;CB~n87DThUTX^4r98qp>u4;~tipN}F-!EN38l zj`b#!uXv&)OG~Kj)mM)*``=2(+f!0@Or=Vqe9VqymrVjotw85LHd`&9_eW}_b*;Sj zP4+qN7F=OeM7)aEy@uxMVrGIIuO>zS7W#^(a~1u7BF|2QAOOB)$;YVo_N-hgOKmP~ z1x`HAP*~o_IWUp3efyd%1 zsL-cH{3de5RG#HaYdt&7Rg1(o3o8TVM>L8_Be3XJ@Yak4RUaDM9+)+^ZA7JY4` z1H6aV(V z`te)a+0(jqGK_hjygu_(@mr%THWm`TI3_Z{eN989L9a~mPkW?k$sr#rh2@9%(ocJ6 z*7AALs>p;FHsq+t>~d)|&34C8R!b&SnGRVQ{^|TG9Gt!;RGgGuqO4m;wW0D$G!iHL z^VnDL%};fuTFq|DJ-x2z`3@M4Ya;&uR@9iAc&@TnC_^b+4_x>9)?7FDK1)TZtk*?< zxOSrtQp2rGs?&nCuF8Za_h(0^>DF3QjU;f}N9G(etCCNy1zx(;^y?+t3|8cA$q&dU z)9P!IZFX~F0@?&l#x^C-w>6V<;!SlV`!&9YkPrIl*_ny|0CWy&C5wd=`FAhsv#C*E zW23wA2DrDPXd$(R-A?Vz7tRN#(ynQm^{%Rd>S?YNEStClZvO!5S04g+lIk?NzY;`P zov5&uCZyk~$8+RbL}DaiX1$YYA6`1sYcx&DNl0T!Imh9i#B5 zpW+6OX>wFvObCznXwc;uzO}@SDqHw+`lymp4&{XiADut@GWy-)kR(`8Mr9;^W~x}a z$tM_@{a%&5=5-741_>D4M`n*GAh7#J+>fO`59zOB-OR_Yc?=P7BCSa-S}1&F__A6a*y0;94#q&cRNwztE=WnzqEAN zRFcm+PQNeV-|16D<2wo0GV5{O?fZh#-|msqt$FsD;JsXgwY@g3i;aN52H$Gfw~I#5 z$hf<^L_5N=Mlulo6%_F}n{!E9)Tc^|_Y=RN-P-u3^HG^&w$fdl$N|Y6{kvwDP}jUa zqUu9O@ch%c>L z$L(6n#cvv6fjM~#daimJ?3ORt)9%bZsRcb3-MRG_!v6pdd_C~?hb8rd5)$(;xf;0h zBogD~MNmjg@Nw@|;k)sT@V$mj3dq`dWL(;Lcn%LBDL=*SUnqEQ;*Z6j2kKH^{5#j@ zwzigkGI4sMWu8o9djsDcFnd?7_=ooCy}Iz`nW5S-upNMtq9}eqNTu7?;n%?1jmxX!Cw|}Q< z>+Lj;gkBu8`(>~8wcDx0b`Nuhwv7GfE&b8yT))H5+F#*6#2sc218BM?sd=s5UCN7d ztXlwX7a3+RkCXw(Ue(_Ccfq>mr=&~bdu=fUkX$+lrH2VRDZp%Xe2-2mTrFBNZPT>g z*IL@$ADZlQ)u%YLs$M=uE{|_&-ZdJIpRCk2TkL!%Ofz-KC<=Y_#i*n`Vex{^^MyC}jiXJGxgXt#~I} z@V1dH=8fS?KQd)mC;LQhu`&MieD(Y*)N_p|du^+F>GCA>~QUM9ou$t--UO65b@u^+f61-BTD|)7?F{ZHjsUpdu~_H_*KxWgv4vi zmHh4aGHS-PDOo3{+*jp>;xC52HWPh<(^9(Bp^ZGBA~~>#ze9p~s{RP@CyYKG zc+B{T{ukX_$81%hov+MH=Nz67L-enpZ}i!GOQVkx_(gRowyP{jrbeE0V2&^w?|wDI z-FRc;CEto8@cq4yh&5PUcX|4p(cAei{n8j@)K$a6ok+DO<c0VfW_ON@l<>+A=a!pyTUse8W;`Ob0yj3UKQ$xCyB$T(EBBu4@7|u@~)!ld> zO7O0&V327)v*qTosl^hYneK56hdh@6@m^PF;wvpSHL;ga2Mvi~@<@2baey;hns>!b zTf;UpXnr5iq>$c93TkoR7^PMAIr(|*T(F%rE@?IA)aaCFYb`$m+~@G8hI~C@C%XNa zAPGC|xH@#JD|Fx$>J4+!{4>=)B*&;l;lBc0_*OU=BFlLL#l1lLprQG(>S|vE{CDwQ zwW7nO=-O?KwD7Q5CH~lr-P<2CF>XQ6>sB@Ij&jcF;T>zm+L670muI!Ug@DI%x9@!o zc6&N9T*;*$B$wl%%B6`;_Se<3$UI%)Yp(-p@@YD*j}`cD^>@|`>>DGHGIN}DsBP}r zduXH6yd50L0S^pQTMsXHp$Dk?)`y989b;D32Q0G}}#uMu!Pc8;F(TSA4zdd;kw zS&V|N*F$bw&;!kIUGp^+r%UQ~RBc(mz=920{{7lbbK!S|t)#})cd!J2J%wiLTHlH7 zv8Gu~Wfg&DX>4@s1dj#3i{=L(g?4wEovhIf1O5|@Ftmy@9n{Jk4@{B8WBrcaYr|-D zD~X_7#c*skF$b{(^%PExp=AF6CHV>!Z0^>dGm=jpK8K5&eIoKQK2p*~r;gs@w0tS! z&xW`9jF-MC{@9Iog7U|1(l8zPQPa}BTfu$^{hRea4~vfxXTqWmDa-Ds}i{ z`zzlg+r*ca63YoOUAxbQ0R7>CMLZ@ZDRSAbQOgsCvs|wI&m-~oigbn4S$-JmdX%;% zEzq>GyRx3(qvI8d{y*_`lyTbI_;4(_`JUm`Kp6YmMDy_$YV$w`^I=s-tw+zJ|;y}$NKb1vKt#iF?=v-YF6q391IR60nPJScn z0y}s#CbsgF`Lf$K?>V!<+Htm~ipR(EGhEHCT%>M4c@VZ=&poTW(7Yw7Y7imt{MIu~ zF(5%D@}mrU9+hiR@VATfmrHBiA)}T-h?C72bL@H=xZ$cLcQ)_$eggLKS4t@1zu^_u zXPIvQ0Je1$f8luU3TKZ&S-Pi#ymuo6p7#01a<3+?>~ZPa@~=R?@ZXH=tbWlwywSqV zgFIVV5cgIip{TU2a>GowXzz3lKJsOhmYY!yPy4Dk2kTWVGN}a@C-;)8N-Nz+^KDbX zJ|mtO(D>6@)0Q*y2(?%Hr>gbND#wQ)@n?r`+fmbgBOB5bs{a6K?wQbkb#wh|&>O@{ zG{7dSrbXr*$rY`t;CACVHCBBePP#5-zAGb--#k(L&-KM*uvT}BpZ)~x_g==5#a=v; zT6!F}gS>g;9TUV7>UTaOx|USW&*w6!Bw%Mfg>LKKBDwK~n*z0g>U zGmpEo@h#1=MW}d=Lh4C{k}#>aw`zWusra+QlZmbEW4X%h-69r0QJ&S`!J~!5Q^F*S zE_Ur4R7XrPvKZ}(uRq2C6`I3K+9@ZgKHfI>l05QD-xuB7HT=31(*|%%Zz=hGm(Wyq z-Xif)xsK7a6A5FwCMs~aI5__RJlCK|E@6&OvRyPGfkU%n<6B+QjJp0A`e?;>=}aJmZ2m%|&OY-@rCbj9JccxMY4bB~?o8-_%mx$s}z- zstd;mjAKLxYW4)y4hdoMHUO zZ!U>xJRn`eY)~BeZPt=M59qO+#cgT`$puFwRu|c|Qi_s^U07ocA3;$~Caq|h`y^XX z<8u}ae*s&@apct*M)6jQIv4QXpKzFWj4pG4am7VHgrS+kE~O+&y!m$Wj1k(o%Z*b1 z0PMos?C}NeTrsCMwIB*1@==s+17nPP@kvgV^+hjg*$v=CadW-vVI6!%2o!g8A zkPoi|txUT5vTTapU{4Cg`j1X&+KX4QFWN=u$!)wRVQ!g#1#!ZS%vXuse)*c#?HRcFWym@7r=bzDquWS9`9PDO#MMDTM?v00CGg#~%kGO&BX}9l zKyy;Bg>BI?v+{bKypMiplG@=iKGPEJY=sZ}sS`{RE^l=*gc9E?6ZzCyJKJ&DwYl+U z{2U9Vt+)IW2gY+Qq2>Xt=%Z^#gZ}^@@z?XyU(nC|919({{5tQ(Qb%}Mzqa&Wmpy;e z@ut!;SNPY3u< zdh!q00^yhTZ!x4%kNCKLGs^`mPyAGxjeCMh&cD4N4!o{0UykMxQp{@K`K9@@_Iwn- z*y-Q0{EX$Za4;E4V;Cc?H0j_+w18M}IpYB& z=4D`U$ZwY)g+$T?2dDszsy*wPbscLdJJ_`drF4uG_-XL?*+*j+gKeOSBZL;@sr-dn zvG50iE$x2H(Os-$93*!q<@c>}V*obOlfdV_F%_^*z%qIswb2}PYErXzU5WawDyvjK zLHK9k>pP{pw%6}wRTwt6Gx5^5Y&;1OD$(j%g5p5Ru}lEYed`SwqhiHKDmyn?)zWoq zyJH%x%*f8U1m}#`sZTqHv6L!C+C7gOFv?`$-6?FDr)s;NCEbj=jDh~ueq;Xt!C^EH z7wBKKrPqozT?9+MDeeWf|Lb2hNy1MoQy~{A=iUpU;OUnd2h`EommNZ5o~0 z-&JnR{zc%f8+=cZW3asLHkCOoFRE2Ee}6``vG{Ecr>dLPwYXbVR|KhRmB19;S5WX? zkNe$Y$C@^sk`_n}(v@OQQO{H9S>yIz@P~-?2{Wg)z0w>@Y;T(g>Poa^XMzYIk5O6P zH2sl0C2Qf?&7Og69n4B(Nnk(4*QPr5uk>ylV~MLzpX%4^to*uJE11>bdQ)y3-4}vnFvh@wpb`#yU7#ons4O3cr{?)G8V-n6b_ArF}uOr4|#{#8}2APxgOfQ9eSivF;& zyH-DWjYk${OJz&)9ocNu+!b_=Dm&mg`LL@pP8dNRqn%jHjUb`&F-pJ_^&k zW3GQ--do?y^RlcTV8;i!eDoFWJ|Fm3qWBQ3Qe0}cMldmM*O-eVj)3B{ql5PAne1g( z8mT)+bI0`0*?+}X5l1b?t*65*d3kNT!d(LMgX(y%js7`&Jd4MF@KG;?Bg9?^{@3v9 zd@AvEo~+keZ=Gfgw;FE9r6o!N;U5ZlAzK;2uhD9$#{BNJPCLTn*+w39aa8En zg_mEfQjMG~d0q)hPerO$eNVxBM9wi;pAk{O;psV5geH<*eWX^_*6AyrZSaHkX8oQ% z0KL@T1O5njTf!}T6ka!nbVzLs`02RDqcwlxzrf#ve;ss4{5A2P;ckKAtv)G4k=bkd zTvt&m4umX#fNKNxq5ZDEXTRABTPrV&e-NQt>*9R9GeNSSP`ZhE`Q(ZeW<>3{fHQ&K zypK=+0D{PV!a9_qD{mh7gNaonXYgjA7A?dL&U~MhMtLUy43pBo8i$1bI%6@h$J5I3 zv#Rw~6e`MEX{7hlZO_i0LB(#no5R)TTfSLce9v$AQ~Ngl&;I}d^xN$p;K#v#40ux0 zQ~v;vjlYNVXsuG`<=gX!)NbRBdsj!`-v)S3;opUH{{RE{6T_BzMvtXiExwzkY0wCu zwTXW0aiZ|5I{{vW;ctT8F7bwmZ{l4R`$sx`)Co1U&WGecDZ=-l;Z=mkjZ4 zB5g|xR!!>t**&|SC#-(Kf3Zi1d|Nk|q<8nSK!(slhilJ68huV^rqpg;&ZESh>!cW;h_GS1Ds@&_J1pWYc zL&A5LajbV&ULVjPwUSf>;aQ_lNEttwuCDXIUJcYdWn=LY`@+^UYr18W_Ih@&C9pRZ z*7Gtnu_N_Va~x%W;FMhS6}x7-a1A!6aw3K@tt70#1zvJaNaK#Zt1?d)_(NOPMwQ|H zQ%|#2Z<1@vhID21$UJAat$jxh_*2LHd6mOFql<*SuQV@GtxssuZVf+jT01pvYczX# zF9UHm6JVBMLUD1G)#WK)O&#=qp2a^9d_3?+i#7cl#Qr4korV6T;p?}#(64T-mF2LR zqiEVlSn`Zf-3c7+VmcF1+I%qhePQG4zlxS#2h$?)CH;(cce?C$N-n3jjIl_bOm1XS z%tD?960j zT92E!q<2@YlD9ks0N{W-_phXn(aXgM;hT7hN(pk?+fLlA^EG8n`>tBGwe_{nNB$vy z74xcbsZY1XD|fcA@2S9ex56G8@!y7Tyc^?R4_a!vPN!_v`i7gRTZv$~g@zh8M&Xo> z2s|3(J`w)PAG1%uZA#}__$BZI!afwcyNy!n>%;m47P3mKoCavr5(Z8_r`Ej&+g6g| z2<~tE0UfkHK@BCgupUNn)se0u7-3UcuNT-JS zKKPwS2~!P68A=pqtfH$`QJ46lsoDI|tb8luDs-JV&YFwedq-#T<<#Q-EPM?3L-FH4 zi^9JeJ{M@7E7Ksj6T_$L`eb)dq@Z9pM+9!c>r;F&_&M;q;HQR@!M_N;8|dB`(qndX z({wE|J6IK%mM3gr4oN5KPpx`Jfq&plM#S9z0K%x%W_a5YM!K#`0o8`Waro2pnS2@I z3)J$y5^Gj=ppf#l>1KaM3Qm6t+x{ir#Q9)jg8JnrM)y$m)p;-9m)UaP&c<)U4lwpk zlbktLw&!-grzybro4}qC@r}lxbK<`Y+3GTBH>-K3wd4W*&2XTFl{5ET0&v|%YM;dK zgntY^H27Li1bkfheWZAsN0wERO*356(&h$rIaVm(jDi6N+P!mE_t>! z)x0~J+TgHI$Fng!VER@ChrmCNYjEG&P~v9a*>i>9Gn zJ4n+ZOL!p*v;c^p1psywN#eV-K4q7P;6ltY@=OL#*VerQQTRRL>sz~d^_zbUOp#=L zuv|2;=e|c(?ZtIEN5cOAgsGz$d`aT(3fW#Xeq2y#I$^jmp5$)oaqVB%90QSN*$)We za_nnTuSq_4cJADzE8a;rzOALH`=1QqD&-jl9|?+TQ;K(Se9G=k?`yr!Acw@_@h zP)krrl3$Qyb=(wux#%-gqKo!pzwoiwA@Gi?rg_t{h8ls8pw0jwp8av!zORr7BG@J+lJon8(W8vLHQoJ)YrN!jv z3He(gLF=4!tzA3fm-ZvcZE4_EX&>YtZHd$``yaE4*(aAK$rv8E zCpoOSu+r93(CGgFY1$}Z({5~aB-5d?R+Qrf--6if>DM1xhBduu{>OQ03x?YyB>cmk ze+t>~u9c~H{@r8n-L;r0D4J<6wGWw>uHD^2j`fwVcv;s_mh()WCW$27bXQUmF8tsG zJxI@ERGWfL8Kk+~Wx01!pH#898YQ%rZ4@u&Lva*9GbtUwE)C=j}5!Pl& z-Eh+xQXo(cK|TGAXX-u%@dlru*!Y)CwP#D1$$PswJjFxZSYrd<=~gwL68KL?wuW=y z{XS1BC2N%{E>38r6@T~w-fP}IX|6mced61BR(Q#| zjNAu60O`D*wah)Gy{*6Yf{5RKR0GL!J6A!eKf}KhX!9+^NpGmdAeProwbPa~Mq$;Z z2OE8>n7E!TGH5ODJP&ZHj!d@}ccckHuk_nT@Fx;hRFUx zw@#Xxm9LkqYdUV9_IUDb^ynH%UyOn2&%I|&RFwObH5k~#n%48ng>7e$6->VTZ6`fP z)Agqx?fIfHeXKu_zqo=ixfJ&%t^~BQvU};Zt2oLaXd#^oNaP>5alqr6g3HCeBh(yQ zMvz-Z2@5BdKX?yd0IlZXbfon&i=0<7TN+*y@W!Fy%}U$D(%vqgHPAcmu5crgPT0Wt ze;VJq{hhR%ZEAf3#do(dUqGs2OGVhT0(sc%aB=HgHN-Z{sG09CZl#tW42ZW5rB~cm z)9W4>)C7%p1m-BA4FgN6JhE^90BAA59>Rz$G@)s${K@u}r7OYNc_V7$K=41qtAQQ< zlG?&X>?DGHST)3d-312(3gmTdO4@7LUdO}lYOf(F8<0Y&>4Do7+xUONzXfis`$+~RPhIeG%I~gqtYig3ZSwyb1V6a*mdjN;wJskg_nxd>o8d7qrAVs3mrEdl-8=+`XkqplUb&0B09=u*eq%;^tf+ z=cZ|sOZE%NZM56lJz3WG)3V7WQz7T)~=zB(qrLAW1mA!mrI#tc^6o-RV&GS z99K-$Nkw#KDm3kM%E4|fi$M1btIC;JkYvZFwO5zIPj5P2TWGd(3DXB<*~;Mm021~l zopa$0FIO%7sp6Zal}eIrB?>*4uN9^vw6lq9EdpT>0P@`KZ*!ixtYIE)CbrqTGUYU( z?*9OhLLE5U5e(W^*xQ22Pe^3YW>XoxaQafw+*{<>x+w znwA|F!WR-teHkn^WtSjhw>>L*RJAjmNkyx>745t*i42Ih;$>h$8@D8Z*Mr;Iq_fkc zOJuc{{v~F}4re5h>S>q09<`m4b-t5obXerV{xxFAXo5ijn5%$5!So!8+A)l3a@dK= zQde4weV|y{SotlinPgN9#v{G4>6}(9_7E-7EK=RR04mE7U{9g+u97(6+7=|3D{>e( z#y#o@^vj2v7Mi8RNzf@H1fKr@N~zLPmHBRk8S=k)%a&4-$7=dL{Dr|kX|_xNM{1q) z{0QWfT{4c%7$*m}6>+qy#r@P*+vAfQ;WTDe6_O~{mH^+m&{!+Q{T_eYWU8~{(h6&1dQZLt#FkWNbn zkd?=^L4McrOXk?gD-d(#nEL)EsZFf-(YUqJ;lj7fr>P&(u$1Fsl{w31XvLxLxZfP` z^Vf`mf5xuIr_Te3{>d9~N0_;+TWigBJ99GVN|H=+;v0|v$9`!qC-J0-y4LPkFfxC7 zr02dXSk5XMp~{q`(rqu9yA``8H%iU(U~NG|CDp7Un#rYOAqRlC_pE&y^Tuqa`%cr& z2-{ZRft>r}x4mi|Lg#MjboOAW^9DH2y=xkd+w!Oq9$G}M)D zGta0PMzn;9x)`&a(_OR zwH%t9${I^ZQ3&N%IZ@iAv{_W<4Pp>A6VW>@su5=$HvEZ;R z#(JDqDie)PSxC20b7+z|6!4~^EyByEt;R_wY1&)x6(VW3_MvRzvPcT4I9AC5y2pqE z<}yf0>fr1H@HG-iZwL~`kfSi=*>lIat(6Ap`4FqWk;$%ysa@ttuUS!!6z=MMDT(0m zX7XI!&y~)0O(5C_`R`lvTv|-bQt%eYB?YP4z;@3}R(`2{V?N~m!C9E(<=pKj(*n7r zhNoxD&3Wp#?(AjWYj*a9(!jfs)S~WT>A>Uhu3Gvl`@7RCcyi)dSPjPRU%Go7S60oa z-;8NmbXMv|JCvYbZ-1?0Yg$gLs1-Voh~WyqDq4;X+r5*YC$f{AYx0LjN}fPt*HDR_GXnWZk8CN zVY#Gln?Am^w`r$nx;?RrQn|SEghgYX1ycF-`MV!l;Fbb)Q&zQ({cyGeXlf8e{}{nss8|KxOkJ|CAPhDs!OU} zUwxrSlHXL7=Z)FANwJR5!14$H3MuA4*w|H05ayG6u8YsUy}t97t_5OibJTWSkGnis z`)l}5NVU5cmMdqaxQs5ZG-%i-knB(KW1y~A;TP>^;SY`96z#klXM1<4X|uGxRn^2K z>-wieGMJI<+)o$*il1=j3TwOb&ws<>9+q?Nz_k?^Mt^7RkTz(<& zj<0p6UP7#kJ-nC%I^mR$Ph(zmvwZ6ti-T}VmDe)r)%EhVyXthTzX3{=qZjX6N7?u4 z&%Qn-cm&^C+IWM)J|UM)wP_wHV|ln}`UzBLkz6;6{8^y*8a5FM^*EG(U-+CbRG?UmkSrR`A<- z>uIOJtYC!w-!%6=)#+FN01Eya+1$&hX>v<%=CcVdZD2@|W$4*b4s%}*e#t&B(S8be z^mvz2irucR35pAwCjwBo`OL~j1Y@r5xv#U}_`l#ZwOMXFZ)K`aC6Tz6+BrkE;z!KN zKI)F%we)#3rHaebsT)l`t;wa*?e{zk!k4hcwFdcpEq>-djr>dFkA_;<@c#gV#B#AMm6z%c0ES=1>tMazX*yCc7$7&3&z@J4dFg zPnT1gHE>?W(q8wWKlloHESXysVc%OtUGBM0Uq9r4nQ5dzj3h~DzG9yF}RCx2xwRyGBw!Oce`%&>@mE|Ij?FJ<%dl8?L=qs=XCB1Y$78s@tvXSuDsIi~sqWCq zsJF=(8n#{)(Qaj3LOZA~F)kS<)Btu|b=`{G)BY4%_`2TWNVSa{PZsOCC%>01F&vOF zj@|3V+r&Q=JTH`Q2{cfoyLU%!Tx516)}s7Y_@-q=Ujf1`+Z%2#2GD=PwZZXbM^*{c zmo4{Jf8;v3Mk@a6Pv&}6hr_)JSlMOqMu!Bktf;uPyWji-!l?`@fYh6y!YuLF9I zUVUpi!&C80;p}y&rZP_^>8|3nL+51kmR`SJE49|5U zmu4wJd7cr7}wVaFt>KoI&FxpLOh?ciD$F-)i){Jb}lcmkDk$B$POQHRz{i2%-NEzo|3F4L17c zz?$W`V|gsQaAOTIZH?QG)k9G8E^T3t%SmJNGNM7sdgS_31hYoJWnDhe7_KAohSQJn ztY+&i?9CM@zj`_ixzKzydwQ}%s$H2Cw>)C$mp=ahg>?3Mz0R3uZ#IRmYLSRo{{W-h zUI}6hKZ!#ELH4eHPttWO^_u48?x8rwF~~hKD?%>_>$2Lk3#!W=2M*D$bJ>P}S}D0N zg$`~rlICYo2gOerTBy9b*P%fuFEXr1R!%yV>6*s7)KkP|<&#;pcaisomJSEDJAM_D zAAx*Kk~9+di5fQNFKPoR^gh&@Z-F(vK4)XB!11$XmEa?fLCthJm8BGvcQKSHMYg1o zYU@Zd%D-mUY~eAC7?Ks*PpIxILeVC42%4%Zuiq*<`wwc4F9%xgA8NLVj!7~p+H=~j zN#SS=dlef90m{$%)kR9$BPTj`vSXyTmMFudrJRBM0}OrrDhb|sq;)YM0ZArF3XId2 zM$|3YzRPszt-u5m^!n22`hKdphfup&hH>&>@%Z(se8`t=$}?DMvRVUc95IqGS}TIt z{OXX?yh5{XlHbgU4mKeq=hRn0CWpE=iW{av6oSixnk}^LLd$4?V*X~+@}dKZ$zh|-Ze|XX)|$g#jXcP0FH3?pubZ46{{V$PUPi9& zQnSz4`gx(;#kNhb&J zrmyz*+U4`1l_MwqS;%*OX0@AxS4!kZne`-(!`hj+X|HbJ+Ejd)>FbkI!E18&3ISb% zJGMkQ6w7Jti}`PI#fbTYlY>&57$uaWTuKp!7s`Gv$QPDgybq^( ziU{oIK(pUFBYevE#s{@hjopy_rIw~^_$J$IYZ!RcE(ex+9>AYUg!o$RWM@`klk#qJ zjQuNqDC{8lR^nDI=y^39>!&*c8clG+4evG)AzslC$^t_-?M3GtR=&I zL-CmeDfx%|B$|&(_W`C|h`^mTWc84Nn(=S=C%1^CllEygmsj$t$MFNkNQ)X0KprI2 zM;NbewTjks4?H(BAK_`fU&g;2%&I@_8k%h>er)}d2?zB`Zcps5$aES6%_6Z>+7ke) zWM{QZJT&Ac+0~toMm&Mv@T*p~u%fYoHIFCnGaf5S$!tOf5@hG*U^;$2mCB_VJxyhF zVYh~F7T}_&JYcqX#d7-fu8MVku-t1gz0TbE5WYVTdhfhLeW!~CxAPf9q>YOI05|7c zMfZX1bz2mZ$$MLn#l(SkbocsJHNLZZ#a(s(0DxUDXD4~P6;cyyRSPgZ0CC!$k|>Ze z#qxveoK&|Ob=9S^rS#rzKpVjO-{DaCX%jG2V8CE5?zEcbZqCN`gVyA7`A#HueDRV; z^r$5g!?tFOupP!fI+!Z&5w=;Ek$YlkUaKz)B{J&3XMB5sT%v3j1ipAR3Ow~tA+_bkF)RGB3usT(n zJB1QPk})4TZBg|c)i~k^05kUJDqBek+#zWT9qW&8m~mXSWR(|Z(V5j(qc28u&~fGG z`{Vi1qu;!2l0_*Imid5g-ROklVgCSVe>%>xycigaWS*Y&_%G4gPB?7CUbO!JDWA?> z075gvW;gFk@~D19e%oFb(moP?)*dLe(=24RvANaS;?x^+<%g5!Z=7_?7Ciczt>H=K z@hqvQ%`8uD+4%~Av@q+AyyvN}s=hLO0{B7uQU1`{_lscopQ&0}A}kl$MXYF&38Ez# z*f=B%@G*{=?_4*JzhaMvpAT*$o5ELKE4rU=ooc0Ay=-s=i)x zut?0K@!SW!e@6cRu?NGk@T>Ms@F#+&wlU8ZgA~_EoDn30dvW;J^XK7@hPwX%{50o_ z{4-(UYq_Uy5_yg%Vj{N#WcZR#LU0Hf;Nric?}Gj#@dt`N5BOtV@eY96W!=_^Z6(El zkT;OtSQy;A5ynM)_J1mI$*3sG{Lf#zPt^N<8&#!-l}d`*MOxO?CwJ$4Hq%1ijE;3w z>;C{A%Z#7)+N9F6{hB)Cd-WWB<5j#&@=2&8O1M8~9Y5b{mrDcepzeI-@s{~V%zs+` z7vKv&q+W0T0CB(csGpf|*ZiGqWB&lJzxq-ZbqM&{-L!ncZ0TX@=IQSn1ItBrAbOKtiTgrmIzFG^ohJ83P$O+y%EC*K zK1Mw{X1-AUyZlu*f)pY03dOW%s#D7|L5)^BCKea-%=H)b%$^hoxPu6=8Y z@L$BW(|k$4?F?jbwqrbCtcjk39CzyS0{F#`<(-K)#@;3Qme90ftgN(2ZRKkpdF>V+ zqH&UPs_!TAS|zpD$o>BR0D?t+*%x0JzhszyXTKFQ-e340Q{_!G#Qmzt+xH~omiY(E z$DyyJwI2>@x|D}gn?Mm+f;O}yROi9iw$gI6bxr>OJWHMhp32tldz;?|S?egVuIR6#cSy(0l2t*@ zdUM5glWKntXKQO85_}X%UO~A=kpf$!ah^VDTL9p8?Osi&c%#O?7J+Vj6{%iZO)RRD zy_8{?rW>*A&uXQvd}NMYHs8V?J+T(60rM^|Z=z#yBOXo=9AwuIfLN?FJTAh1+GRb^ib}#PGulTBit6lz*CgH~h@&ekfUd8qv4uUK_Hu(xPq89nHSAEy*r^^2r(y zql)A_Gx1BqIwXTm)Dp()Pl>{+e`EQpE;9w!|tFp=9Rt*4HJ6Rn#TY79Rp8w4sw1qB(rE0C#5N{3^AVjXY^> za_gt~)Z03Z=x;8L^MS|*kWE&<8djOVPm^A~za~CnUGV(fNg=VtWd*X1PxQBsG5|zQ zcM^R*pGwMArv2Eve9a?D7Hy+vMDbnzg{qBK%Uix_<+)dwhi{#U&#o)dJZ148;pd7X zi&N8nCHSt-P%5kan6U4O8Al|$4sv_fmfu=l_=#bTYu%wpSmK0;#(QHPm8EUqoi5TL zbsveX8c+huYc$U8ht&2JwJPo&lyPN1*JhYcKs=)yD z&jawMHl*648Lw_+xs{hDRyp16SV}zc)@}a)UCNE%uA+)9t1*nnCZTsKvFny*bN<&n z{xx^Ou-a;xy{cZ^MQG_h^q%h9BOvrE?kVw|E_v;&qJn65L;jTRGqLl*Tz?0xO{r@7 z_MLMrrMwy~^bZ(V8rt2=ioj!jbHVnd8dRvsIqJUu05Q&TsiHle)4)FhwQC92SMc4( z_JZPOn@!YKG>{+e)`PYq$fv>YvD0xEGAf*gf!j*GJ*+5qRHPhI6V~$vkeW=U(e->@8C~G3Zo#^H1?ViM&y&TbpkZ z>h@PKM*dtnlu)q^v|xp09-S+dQ>8C^^G8oTNXbET>|5~0gW_!mQdiS7xgeHEnLbre zB#e$%uS4FoB=~>g-4gW3x?B)RG<%lNOBUUodS{Q$xlMao@P3nEF1$VA4S!hjm-pXk zyD_c^>;cHvly65|+fN}cPd=N!$LfT&(JA!0Lv$P-L z!u8LkU0iIruIb%*zw$y@I5njH_Noz_ga^1+}aYqBO^|91bz;Ys_pQ@eKQJFWh0|D;x#9vvTc)A+AZuaZlI7UVok{kM{$hc)cQuB zduwAY)rXECuy!B3fN%30^U!o2=CY?M^jcik@3891lY_cO+iLzSxrS*P*6o(+Pu|XS zU_zfjK;!9Mf5U$Pd~Ve=xAAXK8Fzv`M&Lj`m7yn(JX@&1+J3d-O-Aa)q$W`msu|ZkMls3nU6H8^^Eo^Gm*cPG zVJdTRe75=P_#M}Zyf5KT6h(1it?QPydc!a8FC_xr1CC34q@HSxm%=aE;vY3UU#scp zkVYQT`pZ&s=0Z8%pT;ZAWYa7)3%M>W?IvXplrw-?SL@!d=pG#K?arW5>&mi&X%8*L zrbqW9AE2z|8g;5EN|Ju8X-bTAsMn@~`1eToMwjgtn{%k@fdYYc6+)BW8RUQ~CjQGr zv{4U&^hUK;+y0kzIccL|_lVoV=C?dyFNAzKcW%BCGT+AWg_C&&f&Tz&vinvd>$;WP zjfF6wdJjQX-N)KBSZ-q$(M!3NA9ut2&<<9n!d+583ZL&I+*R}-0}xYMpdkhlEwOmU2Mt{YkK&aW-h z_nsO{ne^i3J88V^NIji<9@rIyY%N=<^EoGPm#Q_PTT4kL=ila2mPw_&VQ;V8tfD`f z)By1ZEyVu-Kb>nVKw}?w3_E*O+sy~WFJ}plOI9&}9prDFJ%Fv< zBGal6@F*KN{?Hqv5No=7+;tUac#lWSu}FNPTgGOgH*k z$fD-X@e3+sMGDBE_YI%tRSYCz+V+}xig=oq_)c1T2ZbV*TZ?viK?${+lkP$5M0UEw zR*Eg|u2aqEsXp?ay?lG;b-?ZJgiTUFN_=bWh(UOhchA#mD!ONIeRL&5s@r3^Ia%Fc3sF@0sb-76SN zW#db9+a#9k`+9)eTk11cY!k({Heb);4R5kc<0n_2M{oJyB^ zEyIQ%IDx|eea%?6yt`xeh;)ms%sziIEx7*x>(i~Ixr##?>KO&VJ7UdfUFy1qm$7c7 z-jXKh#|Loj(>&K_y&GOz`WZIU=w(f)c$Qd0L*Z$d4m{Gs3GPN}Rc{hB#^o%;LxG3q zKT1aN&9qJSYli}3pWUbO$E{BH(_FxgwsK*Ya;Y0|eNVM1HG8RCry0d;kvmIz+e^F( z!#GD@y-N|)=R*|s7EK=`D=rEADvCiQH*0Y$@CF344>N*2Dt5cKc>)mG87N7|MRM#B+bp|c(qxMOe5;+U`BaY-41>s@8+l|r)q8zZra#l6kBqBePgqtiNXpzy7+ns6MAWI9)N@dBZBU0+IBoEEvTbB${J}QTKP8 ze!tF?b#_4Z(4hsDpl|fFRFPbU0lx6{#adep65>7IWkO@_3Fp}Qin#LWGD@p0vl%*_ zvJaGEi+{5`fAS2Juw%nY4hLiZ0M@6-+-#70$#p6vu8(fmirzF}G6@_~J*<(GHpy)9 z6Ovu_$kV3Ju9plewwTjJ9uygKrNF{-sn0@+q2v0 zk-Pr@0y_|({c3%ce~S8cH-$<`S))JgRuUm=Yxv)J!$8vDW7?H%nUo}!v&Mt?qrS*K z*r`9^7Pm2MxY7`D^DI*y2iBi$pxax{!C;w3P0b*|6c0+6D*pu6a_b-H}TX9>u01<^pJ3Y80?nMVRl{_>f7M<>EPZKNlXCYzXO%}@2Fxa%jWPbU0 z9{%-}t>_lB$Yhw1iUO|G#z(OotI!(6LlHB|&A|h5kIZ^iOWO+sQ*=OPL5w$S)JqEr zkTiu~dztEal-eJOAOvV-Vu73Gbl{Wkns%F}c&SKTO>Jgkc-bHTd!JhL1@OIree#BI zMpza+AHtxz@T?I+lgDn6k@tAcNBQksUsU#YNxOQR{bCPG9PB<2TbEV2(&LQalxz*Y z`4t?#5sJ{2h8JaJ1h_c;E3O)R2FT6!dx=>-`!LuC*ZeAsDXHoNN2GXi=0m$+UBe`v zgBkDJ*0+TkQTJxQ%*Hd7MDHVo)^ylnNwnKtJ=RN^|ZS*is51&U!4@A zZvO!8{XpwqRYqyWIV0vP#$3)ZdK;c2@btbTifu7cx zDNg2iTL*(&S&msp9|b4z$8TPS*j#N)E4SHu`X4FJCZpm#L1on7SnYGVIN1X`HZo2R zOj78c6pveX*1SstL0}|kwEJ|6_mTnC*pB(<>0e3O{1)&fu~$tTNhlvI(v!F!T5P`# z{2ir3CY|+fk9~pdT)FIJy$I<*Xr_D9# z8D=U}L5_?;>rD7L`#$*l;)|`vh%BbRh#)3=uwa2n9Amo=rF~DScoV}K=Z(`=z0np+ zgn>{>(GDU9?y%@{TOK9x#g~BXOges^?4btdir=SYRRj4~khngKagwi#t7>aRk~^Mt zS<+OfN8Wu~K7Z8z0AP=X^Xhib;;kZkySX7M_S$^jL@_AuymDK$Qt{vHm!?MqCiCH3 zEv8yPQV3S;;w2x&Asn+GTKW&d7m4Dy<<$H{@)|V!* zw$VocNj%wb>ygJMy^Jw67mGLxo@lH@+AG)iYkUi?P^}L=Lx%)(ZGM7(a0D{WmVCYberyQxzYQOP!#vUoY z)MmHv)tc${>2WIFU0hrTl4U0f%6UJ4uRqg#IXV${Exec0vle@sqDuWj^{>ycxq{2{ z2`Vd-W~Cu#OtYdU9N|yTB7iaafwLn)L_>|Wv*yf z%wpAa8=DzN-eYcAdGuxCxNR=~07%n#)T~Hs4Xr3uR{eOci$?MOg%lD(w{Rkn@}yTo zucuxs^qjjeRViLoQ@P+`u(9TRvA3bt+IW9Kx&8bXlCm+#{q7Iz(w#4Zv|Y<4Wp!Ri zE^*Cd+jzr7nd51%CP1Hj1y*0CM_Sgi@h+coaRt&##{j<5&wBSMPL%AOk0Ma14TOIQ zXfccy+8csV&OxM)!~PpGt;(S#ScPCJ<6I<%G2HT}8FdFAl_!z80g1j|oHt*kbxIJH z$j{qUiq*0)J(aQfTIq53Ld0-INZP%yUnSzZkYfebF5SNxd~n=AaF=ow#$8#eMGi@0 z!CqGl{OV%{m%K+h>8Y7M-P6pGj(tBG-t%rx&Bk{3 z8KIgre=<-*ZQukIHkH-3Mf*i-nZIm}U{!|E-s3x?el~?9y z@^OK)wrP-UnEdw6agoc_^2?Uj2+Au<8FOm4@nH?Rp~%Mhv;P3m&SDIQc{?i|%`>{tq{X2Q{dfnwr#yz++ zzuEfdnTvZpGBU?@A8~B{6<8067WaNsSGsIy%tjW`pNh%0owqef^k`YUGvY`k5-*K! zT}a6-%AcpDO%9!}?ecCsL3Vl!j9~s^uf?P5S9XCfwNk!@1jbL$(P80xyIX?muIdu! zJCa#O-@}Ti()Ihy<33lq`-;$MdZoHCTY)#0K~U(UcjPK*t~^C57({VIpWjLLk?+#2 z!>1StadAHF-^O!_o%H*8ux>EDSSyC{3xK3bCa~yX*?aTT@Baz z;BpQ?MCnsHcX!kj)weZM=8aj%2O*J^;-0<=({1u^ zuU17rcuaGeV|Yiw!Ti;zwf# zd{fsX@{foubo+S@6r@BpJ9C=vtn|Gq-f)oUiVk>Wi~)+C=S549WmHhO&&&Zh^!ijy zR;r)9_Zq6y;O`apoF9ff1+Pbl^{)~^d6CXwIUf10iZ24^?_Ngf-C8cStqd~3lwCy> z5y~WEG?QG2V%qRsOOuVtzynyr2~9%MyMCf|rsckcTTKS)!Z%xADX>O-$blVuR-M1v zAS6pVsRKI#ftta(xzzl|So9>qjDGt6008|!sG2Qu>K{5U4p{EpfPUQlKPuDQg0hJ& z4p+NAQ2zjff%QvEulObJjUtLexa)c$z)zHse;4at&n16ApYUG$ZGI2of0p5nz)bTR~YX1Nu|J3?F{t4xAGXBh}BM7B_Ab7(V#z_AF zgpW|Ka#-VBOdNtwCEM1#Q~n9%VQVMs&kV-Q?>;{9f6u#*`53hfn)ep55`@Vsxv`VT z;=doq&QBXve|i3A>zGM@vBKS#km}a`MkRC9BSaU?jOKeKp zqv^=W=zVG{k_!mNdi~IQ)$LN{Ba9?*?dVGmO=if;Cg5bjR{P7z=DDgUMcC?!=G>3S zMwm`6rd$kc-IPCHdYVOqe8U@XFfmceLJXD!V*}op^0pZhU_x8Ih_0w)OeH||L!2xi z&8{w?6!#Z67S`duefT&eodQS9~Yo%gqPEx_*Un>nxU5Neq%X`I~41nt7%smM1QwDsxg# zRj*4cKP&m_bKvlJtRyX7Hf=}KO&z~4klkuSCCqGO-TQ&gf7@!Yr^@SSs*-{8x!OiY z-ZfubLmkp7P%!}DANJasB!MmCGE5ZibNBFY81%2=?gE!D(knN={XYKy(xQGR!%F@v zV;7xY{V5k5OCuLX1a$`-{VE4CtecpXAn*?yeQLUDcGAGI!84Fs3!XXj=e1i;@ z3j)1K#a-iT64QDy`mU)p-kTk(msJ-^;R_rSh8f39``5<*0Pt8XNWbj!qIfd*SZjSd zU-4Wv`h~fas$B@;l33Az`@lMbwD57yrG1U6I!j|5sTojbUJGZlSHr*XTN_(z-+^8X zvbjK3UmGX~cv7eAli-hWU(A?zs{a5>oVGGoUhm_}g|_}4Ub zol54`^1?~umsQXa7Uoo!ChFa@vP^d)1Rr|Z@VAR#)u9QfUd9O_NM^>vj+hZ7n=I@pnm|L7o^A7u2loOz}Ga21ww? zlhl!qdi-*cZLhTGZ!E5ZyexP5kDbHQ-xbk#2gDLfe7c2;?PYzF4x87e5BOIeal^Sr zcZcM!7j=6y^;i5lE}agX7G;gXVtE?08`Wz1Z+(2P_-ubfx^Ig08GJmNkB2l^W44(* z)w+^)o>x*buB2sKBcQG`<37IrBJl2&dp?6Ltb*ax%Pe6UOe18l0AS~u_}BgjJ^ug& z4DknvJ|bU!!hSwmxuO2cyVU$2aV(-wvLrGpOKwTzC(ew$a0wl&?_b)#2gBM)DuKVW zB;`7h1;^WHDt--jNsQgMTJyx9Y=+6+h z(=2r~k}KUB>gWXdWVDd0Tap-gp5%6_ejELrAo0X;C6qeusVcj9kzdZL4?Grbp!xw` zqx&~QZE>yYaL*5zTGBr)e67bjoAUbB=ZQQstjnuSd!*jq+A6kEKtxc)eMff1esADb z2~WdJ6-jEGs_nn=CH-n?;az!0gQ-R?%}RUi@w3d|_B5W`S5FgomiFgLvkI#$dj#&T zI2(O4?^!Hm-&BSTX&s$lSf?RvyX226TI#lI1}>=DjnRLYHJ`DC*rRaIZ&}@v`~<0K@&~4dL&Dk?OiEy63|Ue%DyK zkUU1iRkAUwfO?}GazDLXzl!{O;=hOYi{Z}{X`UU^Cp%1H}-kdwY`7)Zu3yn9E;OALqX79(rHzbkBEaexkOe8i3Q5%6{GZ*aaGa-gPei`s zD&OGWh`+UFwEo$!(c*y$NcTFjx0rpi%~Kx>qqLted{xoom4hHFals>z$mz#=>R{1) z5#v7=2{rEtv^Vj@7JI3#k&rMvOD+#y+;Lbx5qt%zK(BG8>KX*GTs9+y$c@aP<2VBu zu1fB7>GMXPH>AJdBUrn>S1UhH_%8ndfmcTOVdExd)imu>M$}mc$md*A-G2Vl+#7api3t~f)C6-wD>JNM|q~Uodw;rmcfBY1>js0)Z;u?YiZzb zgf>!LMdR%j!p`k2NP|jSiG-o^*`JU22&=yiHU9vGz7e~=&?NBphvhGlV%6@6mP3#A zV3Co;5If>p>-D5A7~R3(69%J z%{~)1+Lzg5iqWQqMTlT-+vo{BKcy##VbC>wZtCO08ths&ndS!m&ij7c=c>j}EV-tC zW_U|Wz6SSRlJ43?3nbQ81iBXQw;wh;))i+B7caWJ*1mwL%bC0D{dt=DHixZQn~PXH zD`h;E*%u#ay9t5nR2(oB&FZgXcdFe(rP?dq#=)eH(5gVA9YO2(Q?%$l8%1+=&*NL? zkyVm7UeU|?Z{5(3V^%yjp?o4sxgyoPRpJYLKbDduk}^JjkJpOL*Q-~RD64v}_ybT- zlWzY2b24sxLk5cRqF9zJeq##flm6FwbUrYO_wQKmf4i^)fGf~+Z-oB<1hwfuvUJan zcNVwqK#lb~AdzR&(yaKi_G$Qq;QON15RHFHk{>cHS4WIVxc5?{p{>0i-F{~U_SoJn-(xYJLY0p=6?aDQ0^2OwclLZU$#MXWSz-B;9X9qKja+|) z-aFN!jHd`ciKXRqVCx$8sVHk{HDUo>vfHc?s33aaWL93IZ)at19n7}D1d)bJp#YCj z*w>)kYF;$cbea4=sEsDsp>QLSv^4@o9YKEB{cDNw4}rD4WL@hzjntPm){y<6$hT!7 z+Ion`86Lu(CGsr`q`mf*g-<6=k_L+B#y9r&EWacWz`IC2^XX2u(u5)8U0s=@Q2BYl zE>9Q(p{_>D#NHIKifJ_BuObblW6Lo;#!YPLSFmd~zi750Bgguea1QT7?_AXD)2nEv zzE0;%snm{=R{0j@mqM{v($eDTNc#6`>Yr7v1Lv>+nnMp1r3~|+Vlf{Pt$CIUA;gnCv7EnS@1N&e z`cJ~`3trUakHbDMOL*Bxbi9d_GiQKF?tIA>6R@Ed!g(IVWJ$Cl18U@FQ zZSG21q@H53kk+yV48-S>-3~j}vt8P10&5QnXfwb}J1(tQD+0srwofPUtSwR|wV2!9 znN^DZ@eQMe3_EfB>)2e-l6Tv&&nr}R<+0FfekRm4TV>O=BCT;6;7c+p$r#Tka`57_ zH2reYJ4S$63z=n1^Y02P#VO)y`(jZDbmA-M`=ap(cT&$VdT>RuI{1*CDw z9K;88iqzl(-#O%p(N&b^zNRV*mBlL>mb#t1!KKu6mRWEaCRqVf+ow}a)UK_inFM+< zwTX6#At#4D$n-Tr-tK!z+8bN4%8`)_a@!o7^y9JgJu7b8#2Rg{huCU5ADek0U}d?= zN4FiTrYqVlcQI+kRz-XJyQ`g2E2!Zj7}ZrBSwn&NaauPK=)N)3lGDX{?z<(VYx{yj zIZ`@sIKZxZP}j8KGD~A@w(>$rQ5h2!Gii^0do;iu%8Y)5y_v$l)@8BxO^zN7Rab z(wrTtJwLBP-71sTD$RdhXMTPTd@J!)wa4~X#A(IrMhS~iftO$dk-ND1R}11V2%jWe)UATD(Gl&(&4t8eTr#fbYbQ*F5$-`0M|jF zU3i~D`)&T0aU_D`96WNwuNuC7_!#+ps{$VsYxWl=WxjDDa-}0z-`^PZuIIwvw0DlJ z^m%k&u|X^WSZr-AQbi&?js+2+OWU0o=b)n(S&y)qy|&@^IA?yZ{Cx3^DCC5BY4Fk8=nREmrIg3b)5+# zX*V($Zg+g%;2xD;%ftR4hs|3UVS$5gE)#0LsG|)swOzffZG3ikl({r+u3C^RAsT3E&8uPiHeSF0Gx$$6E0Sx`|@h z){dSL-b(lU2N#KJm9=A%RMMfHZ0*RMvO`S)+?WUNZaqP$wH-1awTU#nZqmqZ-pavT z@ICmg8#`P02#fm))ZmT4NJk#OTD1Blq|NhKTfY9eQIU?ng=HwYM#$E6Ec9g!M?|*Q zuhUt3%T;DXSrQi5jz=59j)eMDmhfL$DR;Vu1d!Ru+6PLtZ4I1Rfa#(%&Jsf(ABUw_ z@}OnPqHQ4O1mxE7iniLBxpUeq%)QmFEh1^Z;Ub0%~?+A(X zJT|H)G0f?^G6BT7UYR2G8b9{EUJ716IQyJD#x>|(2 zi(J>#v1zVbM5`&XlqPTtoMYObFN$>Cz|C!(6Sy(?v;Gwj@a3Cq7RKO>*a;^!I{0eY z1}C;q#Qy+)IjT;(NR;sQD<2nWvACISkO16+*YKo1KGCB809og{fw@soo+??iTPP8v zwuL;=z&PvuDe-Cj0C5l}<^@*_eJehCx3-9`T2hjI3UT~Jf+vx5-4^YE8Tr8(_8F+- z_<^cj5{cn^tg-pC{Hp!Nk#g=_>WP(H;X3_kWYM?n66!O8I_;DI-<2sWOLkKH5l&cb zlE2paskA7=-N~+Q8{dy5nG^Md+b?a1dBTFZgGI!u$x29>(_=ifnkVggZ zW-Fb84b4y*2Aq%PJ+}S4g3L;R>@$k71&yQ*lR*@5GUO5>9eY!ZsyS+m?N0V3oorni zX>At={+S+YHtKCQ4w`0gM!_AwYSl8om$2OKV!z(!@~TqZSUil6a`*&&-{HqvDoXoq zH1*Q-j~$8o{=$FH?TD`>E=k{KdwJ_%%9XBD__Rbf7PhfTgteAdR?4V6fBLDh*t@KR2!>amF#eToCP>Cq z#&X{#-Vf5Nx0gEsc_jNJ2P0qu>z=>kS*Y^pU$pmiD^WlFGT&>2an>X9#ou0rzD$4 zEve`hV=?P`?cCV=!H}Zo>OCsDT4~xYp=)7p6!AvVE2YS|WhgrNoN{s6y4%Ro@>iY} zETw+y^v`_OcZh5}UE)nn++S)z?QLRTAtO+j81IgtQua|xnJdU}o%cNl;2wvf+%~DM zTI>3Dp4VPtJ;m@Sv#Ru*#!hk$bDm9lZJ&Yu8#jldyVCq^4yef)Rk(ul5rR+7z#iG` zYv*ka`&Q9kQWj|r`^d~irtii#q2~wmteZW5$37WGO$PoeB$@tyCIHOK(2Sm-_u{=A zJ!&-Qs7V|%qf)HfQa+IX0EK;JpuwFA^5*8^Vis9G*vJ|)$oamUam9Ck26as$*Gj*% z(lqETZ|xacTQobc&HP0B z0PRcRKZO?YYN_F?*<{vk&AZu0KJ#r7b#{^d`kzzHZ%(xa?H6xTI=EV#x@vuWCYqYl z#jf~{_G^1^K4{UlFz2N=R`K?qqiRhJtS^T}TXc+NRRcEI@$Kivy9L(tO&;#v z!_5$ba&WI1>nQfC_n!s#$wRMMj(y5t&^@*=x=5Xl$~CXH7Uz_}y?BMvD8HcHAtC@Yr<)eHe@%MUxn<0oem6=b`5r6+yfk zCxvxcH2rJCD{XTVg`Uz|W4Rl+=D$(f!%^g}pJf`U0dhbCkEf+_9~ggUAB0~LZP)%1 zoe%e*VQ#HkY`H!64r|V%!MKdRij^6u$$v7k`8|IVyZvFKTB=iplkKZM3A56C1#`8v z7$Sy3a7ww11N6;wnsjM{`KK8_`k;X_wM)i@GZKbRAAv)0C~ngk*4#?S$-$j;^|s5uRH|{k6wFM*Wj}ZB~>4JEA+ZN zj7Ad?Nxd)fW`bSn@JoSlDoVe2Ng2T&)ThN7wX+B=ES69~!($mfm2od^?p&Koq?HHp zB*2Q4!-&{P2Ia@io(>IqR-ZPK(BQJSyow9)0@av6_NRf2fxteMDtOySl0y`d1PXFi zOpJTb{vWfw1tindf-+fK`ihFc(pToSbq)q{at7+6-w*D@=HlMwFarMYZaHJ? z?L)0qXyR09(zUscieDJ$PdEorRRnb3f&~lXFAT>f*>x0FAHvdbN3C;9;Vov^$sNU< zQ?586x%yRWj|jZ78BLYK909N(Baf|TD%GI0iK?MN{iCmk#y%L9Pu*+KbH>$^lT!Zx zZE0q0zuO24?kj;$11jYGlHeB3PRt+DoSqM5pC7(ZM>z-esfHrISe5l!DSStK zmo}3>%ddbss}cw~_NK|DTWU}ilUpt~9tT`k9}kA^tRs)hyl^mZH*~8JX>!H>x5V;9 ztaHa){uFykl6P?YLNucf)`ih<(6~(oi`$k-;!mpG`1L;(%QrW9~#Hm$`x4+2flSI{l-Tk>c{M`c% zYG3UAUIG@|Q+95-Zq>?3sPE~zA3hCU7g1{2r=}k7XCBSw}rXCv%~R)WBwIx z*TtGtw-^^TyMKsejN-J5f_GBo!t)i3rpmIMtM%J{uWpw*10D2hL?$*jnn(3MH^k|wPH12$@XcY8h8L8G8 z;cQ$_xj_54^{nW;SD~iVGv$HWOC~t05$c*$$_ig2hU>We%4uEo2||5~FSIAji0ZuO zZgKckR=l>gBsu|}$US|rR?(E;#^jPefQn7=d5s%H-8gW)eX5=9b1#MKshg-@Tge}m z3}YPr@yO5RS(9B{#{Olwv;!Y7E=kAZT`5~9Q5z$N91Ng2rb%UEA`C$hX8smWew8kr z`kF?hR>umub?%@ko%89#4|zn&WO@Cn9;%0K!SA=Do;XPPY5bLFa3h;Qm$k=4Z|I>ZyM-{Mq{!5)10I z+FxaULTJ7v)cipQ+M7kxtN{b}dW=gO9Qy)l%sP$4B&48>@s%Xx)?Lo4av3rh9x~V` z@TwE*H^90k{HV(v+!31Obz+ifovNLkk-aC1kS=D7$f|#Y?N0dHaU+sPAO5Pwbtby%U$R%!>&~|f{{SLO^Kv?7v$YFd z8g$M5j4bcK-4GA;$mCWUYxnUfNNt8m0i+(41iHQSr-f2j5(6JkQ&8&L-fX2sr*vNs zT3NZbv_gx>&f?!e%|$Gcf;Qlj)Pw2TvnSPWG8q5?hX9U-tG(UhqASRunD9q&SW2v9 z>~+SQ<>qG8lE%RN*vQ~jN4{W6L^3~xx8U^Yiq1E;B)6D{cHWA;DXn%;9jplhsjOvE zP2HL*Nm#ntw2`nTDH#}Kkz-rCfNv6$R{7m6``VP zLOa;Hk>;ldx6M-E>Qi>pMrDX^*vpl4XQ06wOR_aTd4M0CHpAg_kQ(mfE;G1+$f+#P z?_odful|H9qg_f-5dw4r7}~Y?=h2S=g~nksDOFdrseV&GmHZOMVlcFE>YKIQKmEvJ zTznt!p0#G$X1fAi!s&UznaJojfseC)t;VU+ecF^UA`pEN`+y42* zd*bSM83mp^V+SYNyu0ELgW3KsT6ntBHQRY+68Qst;HLq-@tXdR&vNQi6*|3Z{{Zk%e-dgT1E!U$U){{goT(%Q zuhC{$xGwGYayZUws=fvN7!U^;@`ck{chLfk%t>qobxD0?8XsG^M8vnyhFF`iMRfJ$3gzvQ>`tn zt}T?!Zj4aKL1F&UrfaSq?F#hr2N~(}wLZ>LEqt{DIb+HH0BqOue*u5WL}vd0_Z$BJ zN{Rfh!!MV`jAQew{{W>Twxglx_p$jVM)L9-3&+jsJq=ca!x~kzhHH2+GM|~kM1aU!a>#HiOV($Z;A1s3%m4$x%{?Wc8&^%k(%fRD<}}^HuQ+#rppM#9xAsVPOn!uJ~yEw@RIqd5)-mc@I5RV`(O5N@ja)8^^H{A zmd(wSaYwN?Hg_@LcQxu4+MkE?+f}~Rp}LkeCuA3~hcUNe6?=Oc^Zx+ZHam9l9pQLy>?j(lO#7-s`qjIVy z9uPX`zh7GKrSX-nxU*^2L-#O$t(|`8Vc2@tpZrYtmEjEzXdlE^4{asPxPnOFeBgBC zbCX}P)Taph(w+YR1Mg|nsU^!8ThmjX@gKnd01oRC-D)GmvaP^LRV?JV$OH~D-HGN4u#G)-Fk|<;!}s&`uSW5niQ$hG+CHaiQb5p|biWJ&9w6EvauLMY3_r3~+vx#_L}ZJVB`HGU_^4#mkpBPnP>O^;I9;BRB@U z*I4m}jwEd=+y&ZB1kmIY>7QEWC-E-4N0V=ELTc9!S0b;F7=c!{VuN%~wRlVPaG znk_H5`n%!H&x5rMDs4Ah@g>#8{&&kH*1`O-jFsf%k8brJiM}KJB(lGgN&7F6Z4za? z%{Dl`(Aedb_hcVx`JYa{*JavdxV?vP*yGJPW%jMMxwV!7Yj>qfA}9fwkT!k!{&n2VfD{fnFDF_6egATUtmLb}+c&o-A8?O>b=>Vm?LO ze=;ko6`=_!`#AG@7)uQaN0rI7=FfVt{jRjzTe)s+{u|!BS21jrF7MaKDnGi1r_fhH zq<-5z4AHb{mdiu6(=Jd1wYQH>+!=qo9Xfq0=6UpHyxJqv3`(bPSs1#h@Aw}|fA$ZE zVR-IdOL*Xalq6p$L-fsS496E3zjb}a6TsmdSNWc~@e|`e{4AONT{lO(Eh5Y3+}e~< zsItVZ^ZfQA%1!m>z&I$j|ktr|^cq7TM?1uAw>E8!-MaOpYnKr~D*Z{-*kltF2BX znZapgb>7F+l^?BLvD5CMf>du1PE2zKXqxziko3y`0F830QK>Gr>9Ntw+Pqr)M|fAj zS{|pUM+b}iStgddw!OpKw3~efIIX+yg4U8tC9Z?^yhztHR>e+u`P@Z8X`O#|#jo zv4}5sEINCRwdL2EHKpp8l4kkW2+GZpJu6$n`lpCIK33*06JARZU53(mO9xK7PHS3K zTB*wm#dl49Iuj~s!p*5aHT`OOR-f^|_K4Lk4wtKIW=;yajXg|}xA3-4-BbPU)ywOj z9zG`M_iUQCiqNt#M)GANZ>o|G8|zQ;*N-ned1{t7+6b}P07zuBym+uZ@OjU@dCWfz zw0$P=o=-AI``DUuAQ|n+>s5-cj*C#GCnw(a{RpE=4Q^?qpJcE2oh08A{7b7Wt#{)2 zkX;?FOo7HdM-`oQuONYKuP&~pPEabw%s#k1svj~9IuN${d_lj2dSlkLG(Q{Y+Efqy zv*FlpBYs{-ZLR+3J%(!-zJHDCXx)~P`>b=eSKby|*G(BYIT8=DZOeM{D%wYHbOc{w znYMu2FrYC9+O@T>9@=VAMQbLFXB1{%I$138WRzn83C9(Nn(nG^8aoXj#SbUVoRNX) z@Aa#vP7qwTUnEjvE za+Mqz2>v7K(zv^C_$aT7^&5u&0EEB8(xtmezQzp@ghgy)_l5m=>0V=~Xkz{$VI1>D z=r+czKwom--Za$k&Gn#(Vmf^18+S+v!TNgFnU2TjGLl$%r4+T%JNos|))*`T_?EA^ z*-QIU_=``LNj@C-KHA>iLgq_tLO(enm5xk{>JL16n&&)S@k`>SxGW9+k);KLbIWsY zD32=5k+@@wbj4JgO!$Rn(JN`lsUbdIZEz$X-d}9-S}Wl%8VxwNiTpio79+~GB6bI| zspYZnUNvf1>{S;|H6+4c!`bC6N7>%9muAo28 zq<%T?U6kwQ7;15YeyzKpohlAZoqmt2cyCd#kjtYr%-PDPmUj|6an`C^>iSLHnYxco zyNzSGF$J2*k)!-OjPx~;C)#9XYiRCm!k^%|!S?CWtN#FGc#~-BiWHN5V*9Er! z0QIZ37`eDbs9xLI82wf%O{R89ra`FU?B2=KXPq1L$lxCJJ;sd&ryO>ghN*V(#-n6c zCyqazarT}n)^wQT7y2_vDf`l-SOjO2k>BvHn@!jC32nv1mXb{v83IWGW1mm{y?PY! zSZd3fO3iMLI+Sa{XvWFl{T|NpIj$m}?$F7ZMY#;=>9tR9YPGD}+1kS2@Q%YggdBNy zmVpQ#PhV=IW8=*hTlUkgV7RrN-)cL;cCr5ed$nwf@i`t|rp#CG41g=}y}FW5r@dd$_5T1K*^jj8J{ov@#oEt#YK4II$>O)9 zx{=)ANg|Q*h}bDVfvDqsLT}xTHtH}smvN7vs*V;k)zrS^j2(Vx*~b2+W%2P|x3YW4 z;Em%O0>cF>`qw(wgo?(X!f!aET#=j)1C?W`dR z(0F!1pSaH10=4?QB(7X*O~%a`u<6%tdPIv8f^)l)8LHvY^(dM_3c`1&EwwUn->o$@ z8Q{2*8#`;*$X--ie4zS+j%uCV{{V@uu1Z5?swh?&l~q-L{Z(AFqZ?}3E!1r-5q91! zQpI*h3>`=+W5?iqYW=>ZEu+h=x#N)-z!ij`dy3*TO?R`2q zWme@%oU!#dsdXbsr+>U93;CT2_>#svB=}I`xeIkf>er36@Ev}U^>>9vlA1P5x zI&IjLHl3;>sZX17o=Ek@R+CY_z_FU4uhO?wskdf_ z%QW;gqSCKWtdna274Xf106y6@YQ{Twd(HeAWoQyHDR;UNnHO1=ukkhAK!r zM-8bC_NAMFh?5-u09qqDPe`VTof+F#b9rJ&=L}hc4s*ZP+MOn)XkDZ8$2r38#%nI$ z#t$$dw5AR^`G@?PfA;0PvWVx>@c9f%{VSptzNS%4yY?*2e`t&$GBSoZU9w=G%CoL~ zK^3%*6c-mVNUOL<8)qY@<5v<}JHr>$PbEidgBz-&+jtR~e-lfkF-@up5*+z}NFTlV500|FD=)SmP`HpHEPXuXE@fs2bDZJ`muvnfY+G;uVuLo2pQ;wb@K=e<;h^8Q48vnw&> z!hLU3H!0yz1DarCIP zZ}AoHrFN`9p;lslvqEp>9g$BJEwt*0$@W~PV>WB z)x^=d-n=_b;1YZT_4TQ#QNif0R3Q~DWN1aG+nr3?G%%wE$ST~A{=HhCPnz;UB#|0NCol5Hgibs(`obFUVj%z;SThe6OtA8dJpy^xp zaifjWPlhUfWlUuL9Ytr|X!>2ySg$PBP!y61^!#f}ROQf7lq9wM3NZM)MT}$4RP++X z$L1=P?~JsjW@zp$8c;Y=*vHbRPXXzlZ~I(tAdIP0fuzEpe{)dXd^FM`moiwpoMauO z^X^4WNmfv^j)81HHPdDWxvN8|+N?9`u|`==@fZ!io`SFVBf)e0Lcd=J*lBvD-NX#C zV%z|wKZiUYmao|OGsAjtXr|L-{n{Pq(H*$$&32vz@piG{yVz|si^tCf#Z@{poc;G?#a3LfYMJy`?B<^r^uG>4Y2)h)O+MDzCPSyGUo_#ct%1=<#~o|e zMdX(js~k2-9B!Fn;D!gcHQ@gM3oX7FXqu!N-Twf^4MS49k%Qd9s9rG*B90X2jDI@y zO-tgJg>GO=TaOUhTE@%@5jwHp^u>K8HT$*y008;Qsry}yu4~JkA4*~7ByJE0&g}D9 z(B0{_P4yT$l)`$yVoovEb=5-UoDxiWE` z{1c{XHiJyo<`P?~?2;8<^_=s&6@U98dv~`QZK+rw7>*7Kfzy$XN{1SfdhBSW2(1oh z!uoy2jTE-=!17A+w7z%o9(eY^uDeG;CApUB*YTxA#RCTC2Uv#j-G+81Bi( zs1(gY{!4dTNh}x4kZ|DPaa>o#KZynio(MmoDBRVq%T z_Bsz1YInLFpw#Wu`ANfWILFq!bK|G&6LH{+-8vg+9JM9UUOpIjY zeih1mQvHqp0B0{2#}$M+0Mch|L9b?$O;C5UXCQ;n8um>`K+-h*2!x0%f-?IMfPV~E zINlr4d`%lk3f?J15&5X6c0Rva%6V-XxMpt)!dztx!V~DAaW+NyTRns*4;cE@^MQBzgka5d&9<9Z2S|`GzVQiiu@s5#fZLZLiw;;2khRDb# zpIZ6+ZW|9yYK1BhQR}6eU*cNZ?>&4iX~sU*bsK+u{!c^8uKxgL4~t$DwmPndXK$|A zv^(wP(=LiD`{BQksu%WO7W^Aw7Nz1phqv~&@=UQS+)uo-k<$Y_d)L1Bm&daByI-~N z?u@cSVQ7j2gk|#vI4Yy3wQnzsydB}xi%GtLwL6YwM0356j`%$kcg=NSXHGTTFx8x6 z9WLzE{%Yc_I3!(7rBwD)6W64{(}~g|yvAP1S6ewy%Gs+ht})7#q{M9k4S|_zU(Y zyVA8?I>%R^#FrMV?DIXyRE|h-&=JgMKaG2Iarp17IJomiq?N4xS~H5R3>t;o<$b@V zVCee3nX2DM71is(5n!?~i*XIu;Nq=c+gsV+O=^=zCzjiHs_n`Cwdr3Fzh}RN-wHI? zd}SQ|Ak*z(VkEn1e%A~H^l1^fBPO_C6Y0JV)fFc2PmBP#z*%9rv0#!D&zZ;E&!$f=1Ve3QO2=;;-b~-xr{OL^b?L!2Ml799%`cd&MNAHO~kEf_BDiT2XzFg#cdJ2g= zFQm=P(XM(kdsT)kHzao~PB_5i4^MiCM#TKOn{7Qa{HkrD(A7z)Xvo{c8a{T#85!!p za==pC_)ANHw?}9><%c}eWwDy#T&Aws?swylN@HjbFfkoca^J<7h$gXciu)RscP->v zZ39e)BtaR^-d)ETrwdIQ*bg)8>GdNup1u~eGLt>oc5Vi4oBV3TJ`~fVEKZ$n;0||6 zNk8LK?GLaLNxz7-_U{e{^8VRsae_v{{uIeIe-A|?Lk){ebBv|G2R(tsC-!?l?d4fp zAjbs9$j@q$HrD*Z5=jui;3@%6noVrJ;V8yV{{X1VJxbD7VW;R5zTSRi#tkxEPf&2v z{{X@?%>9+83JqKSHf=7$3-0o1G_HfB_})zo=!-MJdulLRwojlLPPc zr3F1;wy|Av2As7+eeT<&N!DlN5846{{Yz$EWf+c zm*!K>{%P`AOsqD*fclw;$gNbS%+iT$UaWF7lQfxL3W1(!ziHlgi>w3Jj2zWDW4S|> zc<@*cmn=m!;VY8YhYEgWSh17sO-@{*<%Y=67YwItm#Cnwa!wdi;=3&#*snoTgOUa- zCUl)<-iafrU%TZG=TS{QsVjkPrj-hD`@>M`tNX-qtL$#edZ043^hif?{XVrDURy^W z{C>pT@>8j-3ymjPBs573Qgn;`pbJbYI(gH=8cZbN1-*ivE8q z*TBj$qWOECK4TXO=|0E**7wi&C-XS{nzl5cr~E8W7?LF%5BRAyAJV;tQ`9u;Xv0f> zPFH|~8Lyu|;GSA{jUo6cs%kpVj3B$xu0A37xM@0Mc3~RKFNpOW!Jj|8B>N0w9`)`z zo}De>^Cm(Ib@XqirG7D+O-CD9Eq&>JZ2g}ui+DP1^@{w2ZEsEUmM^pr4sfy_zgmiY zPQk|bS+d+YE5XNVsV0PDTs^)y0dn8xnpV>SCL)$pRF;i!wg9nt=yEa*TuZ4DJg}p0xIg?=dA6J49b-kZ z`z?b;ktrl8O!8^}0PvdlyPVy^2Hb)m&-Jh8+%JayA@J5Nym)&s_B3YIzk4{!Nne+n zk_la1{pa%J{1@YluN_@vxY*O9C#C96GHGqk$qQXw`5ulhWevip7{)Mh{{W#>6Ga8Z zz`B~<3y6$Bo!luPk@vktdBw+${Afq+Z{S~-kMwNk^{hV-d`9@QYOMNJt!(+g0BPXu z_v_7RTO{$Vt4lX+f7j~2{E5*+!w(oK)aR#~sJ{N*vi|_v4^#gDgjY#hx$mu_lHO~H zH_a`)?u^INA-WuNt+=&Ym}3y4rqR$9Km1jBPs0s=;>V1v?q=0|QKZ2ox&HuFEuO<5 z{{UoXn)D4LNAVSs@7FbmuL~|h!*8$;*1f#DfINAxNlJ!qXuWl>THpFX$(Zmri+t9^ z=KRfc_SKvGU}(!Mw@$l&;XSP z;+)aMTmdSOfPhD8{Tss13N!u{;v7zUpJG*9O+{qmHzh7@X6EMz%bQ5r&%OE`92LNs zo=cn6%(FVGb!4oXla=|tCnqSUeHFFuaswhcIgGE%la5FK09u;OA`po%!03gK6)}z$ z;dgB(?-N7BiWOQieOX6J{r#&W^;D87tn7@$3~~U&XNs|DEU`$#0DQSPJeps!Av>Xy z8SF(7+cK|S4_(KtV?Jd3jo~zjE+yXzs;Jr|UVVDk!JqJ6OEV|He}}r2&9>`1Pl*tl zxmgKVx7yrC89tej&;#m4ed96x*kjbX=N$)H@n85T7s0O&d~f}ZJbB@5a`rn%v(kp8 zr{74Mg|*GT=_7yp=|z7m;xV64`cmLAGFnB!zfWNA^R?`L37tpUc*RaJ(&|6@X|ws! z_{XVfu-x3)Y1fY&nQ~NcG1Ky^KLD`iU*?WK;G~;U^m9u~Lv z(?fIwijd^FA9(Zme_G*hEHCs+Na49YUB$dxzRL2c^1~mOIH&2}BfGP?N19hL?f|}W z2dMgc*Vog>LY>sF=3!EeJVJw(vAy3-HS6lPJ)hy&({!H^K@Ol=Q#n+U7QqA_tH-D{ z_MiL^>p@Qfe0liQ;5{DxNi>KoG+Vm}S�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%0Dx%Y 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 literal 0 HcmV?d00001 diff --git a/workspace/virtuallab/object_detection/g3doc/img/kites_with_segment_overlay.png b/workspace/virtuallab/object_detection/g3doc/img/kites_with_segment_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..a52e57de193e53edbb1a49643e8c77609abdc79d GIT binary patch literal 7165291 zcmV)XK&`)tP)Lo7_9Z3z!kw1y%<9F2nt|NZl?h=@oCLH~+K@7;Z# zQo?^*Yx-A2davtb5&7%!6=RGsw$^H`z4s78O6mE0;`IhJIH*b;2~C$+NWv(cysGwuwk7C3K&g z$Kz3JJ&ptOQ%d{3r4KAf^<2tK&N-!23e&q!dR{E3r&br6^uBFd zAB49+TZ$^fA4~4N_a1X{_YH$|9sGo5C;7M5Qrh%IF?u}D(|x{e8ys0gAbDk#$(cP@ zM=Pbk-oThUe@ju%uJRF)kbFF0`)=Fj%7MvE3@%~5g{hU8oJC~ccM&;`!;ax!tG_Uu zRIIjktDLWV=A3J-;0J&Peqe8_eX8CkszjbXIvASlNBPIW6?=7p{jZ4pem+x5aCywH zwboi6kH_=*Y_0A4j>rWQ>LaL4er;K@*vn9Z`EN2NVF&7ElU|q|PJv(w2P;995TFe6 zD5Zo%alk)VEAGBI?~`R>40~|mDT!a{6F(Q@9U~+yHUy|Wyx(^s7B5)5gV7_gWtQ7%HaTwwFJWmB8HMUYpCJrBCwgJ@MAtRFKAA{zvWH6MccS7vDjl!^Iol*(-SFq^L+=Z| zWf#wFMq!_;IjM5tR)2qesi<*Gyz(YrLTr+OjbN3@hnc#XpT@_@clwk!GP@%CGCeZA z=#F8kH^+oE?z?^}#-yk0%je&eY&qwV#3sfMi;p%V0zYc)F8)jk`WUt*D}w#B)^h%X z8@7&SxP*G@I^D&$X@X9J-D>e@okcv{M*LOvsd5vuA8uC|T$D4z6IR!&9rh`I=JHwR z#~4qxFJaiWRxB~?c9Y4=tG|dBPg#A6P$f&

    >c9o0;s8#f#}V%PVTFI>!!>ORV-| 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ÏgCV9lPz1 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 +j4$ zhrx?oy|RqRl&4E7Ir+XYJyfG2AMkJP%G$VONa$DcQrvd)EmuHH@Y8s-3kf|mKB{=S z??Sm=?M^~T!4)=DSeH@1o!eIgw+qD@FX;Pv3+mZRE68WPCwQf}>*6+S)#K=D3XipdN zP=^&H`au2CQ}J|H#4BEUxLz1{V3fd2m-Cno*#-k=n1*?sa)kU>(fcL#tNgsu6WSj` z#>%+6^WEjcQ{yVg{YH$ZyLpky$C^I5{01y0ISD+)w-veLvh)}?SlUGmr@bVD8qUJj z1ujCJPamvSVbR^m?ymu%ol%SMud3VmZGBepYZuh|=oEUM41%2eRCn>Dl>K`NW_jZc+ z|J^eVyq!y-!Htmu%yCS^{s}!XYl>;%)h_9Y*G;?~t7)#?h_lmRfd-`tf7QmD7-4_< z3yo9xp)+c$HaSn8CuqO%jGcbtH=KNFe0j>xC_KtepN*Sk+1_a}j44Cpk2EA8faZ`1 zsRccNI9Mc+FH#ExD?Q=FG?gX^m9sU#>xkjy3-y#Xz)fWGdwWv5 z+@N@VF`?_3lR$NADo>&zYksZbU&?3IAGX|jxow*`5nKMmj|BWYw~X&QGGCWvLGE?E zEx(fB>j7F^Lm4=(rnIBSFAz`Y@Lzm_d=TH*FMJ|=Kn<2&a+}=+eUMZVv7>+o#?RA< zVL1)R4gAZ>;w*a%KA1YJm+QK2+qNuA&bc0R@pwE$ZdQzfdscOKniB2WUH5mchgEB; zvB~egv&^11w3Je?-Uhos(@OYBbY$kSL2cXihZ6PT7RBVpbvNly+1McQ!&`l@?c#dy z<6DMvo9>V%al5|pM(2#;*^0?cmg-R(m)zxKPHIH`z)ykhw|W?dPvd0uCzhw&k1ZT% zOLy%MmwS7XnN*V6d555ZfxdA)-KS7KATE#WhRpgmj(aWIBjXw69L*!E#2YVLBzfAhGk$-bn(|7yPOr-|WT36%LXE>kyW;mw z;p)FeKwxC0u%9ixKxK;V?a82n^{}#~P(MBVl=!ENWv^P2aeB_Vo`K3^Ixcv48@!^c zKQ3Payz8H(p}g7^!VME;=yp+QqSj=1bGS$+ffOEqumg z<>t-9i(pZ|r&Y)JNyGNYD@h#8%%WnQkAit3VDRpD^>Enk4)k~+3a`JsM=IwsUJL5? z;=r>4q`=R5{5{VAHm~Ll7eX zmSYe6C^=+k#r@)de|EeE3uSW-x^i9D>SeHil~StTqwpT->gQR6aR;nBzv8;oI__9M zgLOcQT|W$RJOR%7P5a<@NW%pQl>s1Le14YT7|^&7^l}&HJw4ee%~$U4+WWca_c?j? zc-G1KoZcH<)>8{aUK}+`v+WHM&IGF&&AO_Hw+r-j$r~5!v?&@Wy0;th zKOr8U;-!HSdh@-r@#icB@!_TZOYjGUyOS_{NRdqIbaN%BFrj1NIQ9KZ1ZXR01=oxj(@FX_@Gt*6P1!rI z>m{>R*pW}z%lyVz9|C=|1{eUT+4oz=lZ0JYKjLad(ICbG2S#pvBy8x zWoC_Jym|hybqe%Wnalb#`}QHz5UjaCne`U!R}>Whk#pcuN=wWWQ|juZ0NfJB65NO6 zw_A+ye!s8ldb{21#d_bt25<220(+@8;-6#IGS8wC#N(S5Y?e!xB^2UD5VrG8 ze$sv<2G_T9g1gb_9w}TGFhRRW7#klSAE;(602&REm)p9&7aVLje) z+ik`gwraC9Yxtwq~@z~fZV(9iZfOI=ru5D`iG{oEyrj7TqclezLflM{D+kkmW1)1 zren1zOL&KW25Uewa!vx7s@RA5yaHGK@*K_-4K>PmdVP7|We}7{~1}*>S z$fTj;mbp-mdw4s~elxgn^YxX!zsvx3(z6rXpG0*ybb|Qo0T*V)$42K})t7NRQn&56 z^Juc#IWlTpklY@@)=HQjBD=!rdc7|DZ|$;wEbK|uCYJHT&=c!P; zIoozs5b>z+7LUOklt;UhQo;{o8rec-UDwOyVz+c9qD$+BZ}y~{UY{!BeNQeZQvdyH zcKe(G{A&*AaeYy^G{#@RH?4y5B9e2|t<(6}1@RQRgc)Be7*F)z0>X<8Y+2E8wg zC&=sAC`?-Em)>u!Ef_sML>!o}Q=A?@e;K`TrSMAX*LOAVqXShGfXezpeK(%kU*@mR zonqA*uwP~|o~;P~0RRC1|CC+XlH?{31cbCR5gYziJHr3&)rlW!$WB2e-Qr23)^OC3 zbx=a_-@pICc!B87^SIqV{Uh;Jz?RZarU?jzCviwQ&WK7W#VLoncylQ2*YEFde*q7< zbC#ch3KbZo4?cP02F88kP&p7OcngaAvArCFS3sl%*eD=2&kX4plM=R1g--b6evGyj z4zV{`?sYKX&N4P0uYJ2<+VrY*`w`aU_roM*+LrT`YUCPI6h<`k*|rTchmlSxVO*C@ zeba_teNymNFvm-XHFFC2iBv$}>5JdL0w8^6ncqrOCpSbj&sZ~tX7%j=X(S?h`4s&> z(-%8=y4Sm=!o&$%$>ixN_dK<2a^i!fp+q2A|^U`R=Yu(ZLFmT)p<` zqhfO5m|8-)*GIKpc|Y6|6EFH#5`siO1(fsb;<@pKr)2b0>kV{VB~C8yL8Hgd7O%v* z_I+>U!Tu{%h{JHG8*}N5jlI@>(Rs9=1nLm<9&cUxlo~TzW&ziYM&M7w`F@^F;t9hI z0xDedSSqooh>*~Nz#<8)UvmIvdJX_>9a`f;B(dBAJ>u!*ZE$V&t% z5YBDe)TjE;K9o;&5>lbcq*OPOkK<7OkCGqkoE36>2l&7a2B&+PvyM`2Du+1FvjGJ* z@XWs03pu@iqB4;w1mLG}&G6F_g?(ep=Qas`nSbTz@54_%4eXEzdpH6`hC~ypYf!p#7#(-zQy*wE`yI-y`CSt71qg z#SB5?2loA|pWFsKasAkSJ~`)o->-Uvvv~sZJj*?j>vHWfBU>Q83GZKafD zSycau5b{Oh;l^#>iryf!F!MambzN<^oO2ZVsPxOwiN8o=CSb7VgcvZ~4?+%}Ki04h z7hlU#>ifgo=T+?ph-3cVU%!73@TAx4L-^r|fST=^vA9q+7E5!9uy-d#{rpMalL`0m z+>C6h`2jP#I6=IpTcO;`Yqt04U3IwZ#qSK}jl7-WzS27>|H98l!Wt|#yc!oK_WdaR z%F6;AV}Hretd|G10cKM&DY96}_>x5ar`ZU>{j1ItUXZC(AZ!W5$K&t|iPHVkqQGYZ zBET;mdy7jge{;44mbspq@ zJbWHh5w0*MxLXXkA>d-dZ8~(|M1 z^o?uH`9k^6Px-4*Kt5kJF5UB!;c5Py5?1(z;c*-)-f1R=e`kI{ucv8BHMj;e@$`DX zZji@o5XQXUWmy<8-~_5Tci(p_s4}21+iLzp^x}&_=rkZpZ%>uomvZtjUjF{|%N%c( zSQx2Zk2oJ?ZH(J*4?1a{w}Uf!`IPh&_tn0Y(hKE3e0WmzOC3Z%D;*@1s6ZV11&#cu z{f?*jpz;!L?Oj7R@I8>909PDk$1ebXXfMz{|jjbD+){)qBF!6w|aKYvxb&^&+M8PqSc?O)@w2_7R%Ddjx(#!st%ia1x(*~P<4 zrN6~@xF_8Ysc;um<7Dc=>*mrA8vt5{9>gILb{z33q_wV?zzCGPn zQtN%1XHerd12sr>b@_zBzGM*l4({^`m*z?N+s|-&7hmiwO0UHWhiaaCjYC;$VvK6v z;i~6zHSnr-Mf5ROMf~!G|`LY=s$ku?_FK# zNFelcrlD}-VLnjB%L+eL@3Bbx<j0}(DU?hMj!Ox>zA^d z%|b8@v%j&cw=_NN>*e$T0U3V#O+Hl~?;SlqY-%6synMeR)OzNI4te?FoFsCU%C8>S zefx>?SLb==oO=3P*ELOky~h-v9q#*Hp|tXy~*hBqL0YNBBLwI|9A0#fw3%s_&$U>6Uo0Ht*|uwQ z=(+DY5ArYke%v<${AU%MXoZ*G3gZ>{qi_}8`Eo*WPM%fTJED>!B^0DN#DhCt z{FaV5e#m*b(@>D*mv33kwq+z9ifJm-$I9E)M-LtYULjUbt^&qH@z1i355y+01fMr{cl2uzo_ z3V`7L;HPZhG05s|&n!2FBT&vd%L#fCdc+1l7IoCc!+}Jq7+9ge8@4P17~=6tyKG>r zc}Zquh`GDmMBeq=&jD-;ICO>P~+0g?K zFh2Xi3$z>W>{Q23e}vh zfP_Qfu@b|?Szo&RV`Ue*+||>$VDc7FE8Cxn;SoBk_;~`p(xNnrbX)3q-?F8uH1){R zIn7?i6rJhN0y4}-wVI89_HRoEoT!|w$8b!?K2OoUFsSZgdA~i3V^zcPx9oPp z0$6FTP{q9{#WHs3!$ne&Z6F-I4PBlyGbbdfl>9u7W2c>_(&w+&t9y|_t?rF!Tqk&O>{mn1n4Rq?wLILe8zvB(?```m`g5a=t&0C?VMvY3 z#FQ`vZxphvQ@JwOlURP9%fLhV#QR-=th6l~@u5`u(7sK}&p3SqU|qk%`4IKTkHF(| zYs-b0bs|;L^CuB$lP)<_8?C z;{SsV7%&?@8|7N8YA+wWEEMkYLDn*$N?ALz3Zl7oza@eVZe1Lgb|3x<>puV_A5nq_Z6?#1kzP(hk_6_R2ZD={YWE1tYSokqf5iolLh? zb>+14yam6zwd!^(kk9g14}5GC``E#8pKcz=qtH=nt(2?LNDjW*n-aV41k)K1`S(JQbfS7syctszY~w4(R&1^Y{;K8e)q*gov@_Vj)swBLEpQn= z-W_!Of;iFJ{%1)AGyN5W1~CfTh@9^nAGzUXVk=>Z%a(?0fqop3^a~`tX0xiFOFLD# z8W$GJ_CSMR9>;Gi1DQt$5ox1~eRV?1py;@5Z`>Hkb%=IvJQBCVIgdb!Mwu_&TyhGk zK29~`8avWK1AFz$&ymxvQ6MHylUbD)+7QSBxmU?ZS)&hMBJkqY|KRKp?tu}dqwGCS zVv0772!S#-h2&u!VbUZ(9|M7`Eenp(MG4C^u!;$jy`s4i0UU(d-?zy-4jEZFLJLIYe+zHY~c7OX_5 zm8skE+2n)I#7T^f#2%<2PadQP^EjAgwoh_@^5@XA&-QDOM6H!GDYD@QTZIh(HzI6j z&eliKr|Qp7i&jTw<(%6lBXKT$wX-<>k$0Gc1Y`@ugu-5Ez zM{oFaH_ZA=FN0v3b7dJ7B49VXEaI?ZD$C!`2$IZpXCu z_$-rweF>btursqiEML)svNKtaCrmN^>OzNF*IJX49#Ipf(zW--2UUCJ8z!5a`0-qo zT<1TuyA)>mS3&WM%N%TIqxR?6*)4x5-cg#;7lmfuSo7VhYS*wu??-*1?o{hav(W2P zD>aY&rS4Dc$)tS7e<_jH%87zq%w;D5@)ng%XMA74r6r^MGqa=LOC~mOd@k`Jj$VV3d#V9g z8p8yw%_LOf_ZEtygFW0cjx%%F5??RC5!K$p6(8w?@hTJ5cKQ4d00960%w55jkwC!JSav{=02KB8fse52NREq9#%! z7tTzQSO-hqiH#H=FI73RrPMUT9SCkXxKve}xmwQ?b1w;xSGjm!#GHp7UfZeAA2@+e zDRk{`#=+)K(Z532T(6?{b9`nec%`4v7Pw1+Dq6$7++L>aE>tH)`)ygJlzr#xxP=Zn zE!97I9D_t>?HBVr4k$ie5=zjQ!*@QV=Li&*sCxBh?3E4&PJvsLnh9G$G0=mn2roE; z4Qr&_LR6?SIOOC^a)c+D=@ zc+A9>dcyau4?z`w1kdGB{&rkJ5+W>5@p-@B$dhi?PU`g!Q$(MCue%TsM&uz1LFTeX zKM;95pz0lui^7HZT5B7=`f!)o+ap=Tp!^>=)2sIPB!*R6d$Em~{}u0LsF3P2#T(@~ zV%&D#yx@j(#Rpm0zjW2CjmzJ@)i|GDAmX2t3{RjerKp9gcI+k@jE+iXG01S1Y8 zp>LEz(Lr3TE)L0YAZ%|4#W;wykB$@{lqnmg zEkb58QY+()g&t<*^yBjFjqI~}JIlx806rx8lLuXwr1G`t1@bymH zQOX~me;XqFPFTJ?ZI$Ds{3EWLpxDZPDgN}+rRAb|`byC=<|9a+Iv1N@0juz_3L%)1 zzx4VRe(UY=QY|m#B(m`>{jGg#R-ed&3C0GAFIt#Te3A{j23!^cZ`)=G?VXJCCz|ap z6Xi^n^+A!>2OL(^&pl4C4BP3ymd4S*_GCC9VRiGYHENM(FTFk1D*oVMzAk!Wwtz4~ zc5y%MO6;*j_N(SEicg|%A)e>sYY$i*VM~odH7kRE5am&Kyj@TE!5oJdh%{upT_Xq( zymy(TgU@2zTiCva^y+drsKEKT?jP*H<#8G5)yu(JAoAD5{C<80KQA{*{dB9~IuKgc& z?)e;Cr^9$Jm*3N@KgAyShDjtRM^oJvIOTzgx4MhpfGA zLy((0GyO27l!J&NbRdb#9Nc9CfZAOqrpQRAqEAmKL+gb|*%FLrfYrx;UXLNyUlyIr z*O<_toOMSMOfM~y+J{}J|pivDw9m^~wrt+|~uNrsr$TtrfjMIDr`0kBBEc`X9XjZfQa-$PC zX9;E!nemano&7#wfrsU3{&QXR*cVRHu|bH1Y|jxEcb9`+cy__6(IiGhpdRajWU9E_ zLrv%XS2^uPPQX?nPe@?Lh=anFKki(e2J`nhVjH((O~SX1=TG$wcbaFXTxK*M6OS42 z-bvk9s{k%{JpC<{7JLzf3kTZCbSOXdz{%R z(MXe;0mP<}=WsJZgM{Gg-k2TiU@v9v30JQ&ryoVVTB+op!}s`rf9nqzvvmm%;2S7G z_;>O8N+2c73j8i~aPI{IPCylTzmCxhII zg1wY9Ze*C&%=7hFU<0}<9*&ZAJ9nP%rQ@5nJZuMydTu!%)W=c#c<%*g8V=3xMU-do zGd8WVqdyD$a)o$%SH2bIc`T*|o^G~n3)_aYZJ3%R2#`fl^zCoQjz65XvsivNmrk_s z$Sk_I9$7-c=|;)P0$JG}uk&~#A`I=n&*#%z-Z`+m1-Lbu7nW+X3Xfe=2V}pmMJaW`(|f|(!)Q;+o09e~wEK$V!sD5C%4YJw9bt4^iM_8!)emIe z&SFh|J}qBA=5JHEUiiU7Q-9(H^Bf{0MyCHk!$+^z4SIkm=ao}Dw$ z&aqp%&8lgvS2n~$4;+lu`!OZrZ|6zzu^t4`U3s2Iak1pJ-@zThFX?8$Azc%=eg*#< z#}v%0jm8?FPj_K`Ld>$EHF-=SZ{OoQ;pj*Y*Usto%wv)gyAt=ue5Ace{*n)yjrEfy zPUa0DR(7uZF5v?iIs+3Dy(aQ4T?9EKo?c35RX^;f?AHe!x|(+tz-L%Gk0f&QCAcT} zkIMxV*K?CdmufQ(wAQSCBvDgDP96s}@O1UbDzW_5+Z!I$s~uHcba1iBehn;l`Q7UT z15AR}^C%qkzQ&(V?Y@zRq1~ey;^~qhP!g4|b zNk>bS>vmNAcW!Jvlb>`(xLB`fzd3d(67#bLhShXmR<6#mYB(#9p_H-5r`6eQ+dc3 z2lNrQWt8u6Cd2)yTyO5eOfL9qVFF;!{@pr7r}p2IYR5!aR{O_a`h(7f9_P3WZ==3P ze^^25J5A81lArs{~eDm<1Ak%Hl$2K@mVz?sFBs4sE*;QYn{b~ zt!zlc>m_0hT)sK98P$%7{!2Kbx0G@`KS#>Wv`Y~K1xbHocz*^bZ3NVRoT3i*pi;h{ zj)TQuy=t9B<*{4wodttDtdK@zG{^~qgl#*8++`E1*2SXY&PKV6Av5D4N=}FB0EFk` zQTlW0UZucKovA4L`S~d{@>y$rJRS(LHm!%DM>{aL6nSz|@${|~CDA_@#Ab}~C2oS! zC$VU?Ch|;GZW5=4?Xrp|t0e7qH_j&aD{aLWQ?kh6(zz_v^N@e?e$tLj^kS^f%r}fy zmAf2w^dslCZ&(PwB#9Mq=AASD5~=ve;}7h-c;tVeb;WWN`=p&xK->(sQR2G+E2HUv7(;{#IxNr&mnneEL{TUn{zCUS!tF-Dz3~slBvzh$| z00960++Ew2+aL@Cd-wl8I5qS^^<)r|0)%XPs;6w0fDpP2LdM$)+@UnN1HKC4a5Fv# zls~_}o2$`kt+IPryIH&^6+v0=J?gPhT6$vA?5DOnTl{(4RINHZZa+9{jOdjbAVZt3b?A_$dI^7#zptl8X*zn7Z;&;V(Ak1M=lo8?feTj>11lBaJqdwOU`pz}Ph>k?_3 zg`pgV*H|_6eAe!iMm<%?;p*{tz;Mt*&_5VZcK332gYxn0Iyv(3$a?~k9fkUr$B#E5 z^V{pX&hz9{6bI(iW}+p68yvWM@{n%OUM0jUVdy@B^u^6s4qU>W+P$|oFJ+IB6g~sCMyQk` zcE^?;m=dotFE=Wf)z5?V>Rk=84=ODl*b8c}rHz~u2LuDZpnt|- z>2?ttAF$a%V6v2gxA83Z##T0{+w4*nM)dXt8>x11dZ7~~)e$hT%k9tNBF1b_FyOSu zyjb{|o{?bpTT1cXc;FFk;KMA~Z?iMU(7mX)6nvw*;8!eI`TX_uby4k_bi1(G^wuSj z!cPG|leiG)O~vbbmJf_7bI#X&1D2v)R;&uvp-5^8Fkcq3;=d1+Qf7o>_>sbBk31ZH z+<&TQ!tMJ*j#;WL>Q483e^GfBaIEPM*~SxrsK>4REM66_2OXWM-ku-u$isVxAmi(p zt-M6QJU+h!lPNq6f-bL-a6K5=TC4WJ|}ieX5v-$xL2ugcu}suOl&VM|Ihtw zVCn1!A~6k9?ard$g-ksB(uTpuOCiOt$7^k8A$O_KxJ3KpjXLs~zAK$SOtYf8=5m-I$i}YZp3(LPne}c{{WmIan|MI}ClKpyT@B z_3Y_`i8C1)CXSD*4lNV5jHi%i@+u>w9HVKETLwzujl#+R)4gjpAkr^8)2| zCyqH@jy$1P?6rb%y?B8y6@;6b)Lt_E@B-y7Srjy0I9K{;Kc0OB?M=TLv`^MK%gDJ5 z2givig~WZ1#M5O0&j@oIVc|n&r^cHjxS$wUo8;vOf;_hEHzseeR<4qV{->w73c+K@rkO90zifqVZ4qDPMDTa zBo3WnkXd*qf20>4vGQID%+MnB+o-uf&?D$wKElMwS6=>AB_79=8Cb`PrW*%|Nnmu* zmmr{)DQLpF)_4iL2xB~-&-ITja2I00EEcRcz=11bVR+@w zM*A>q@Zx#FeKNIpfO(ZLz(W`CzEi12bL9CW@0S&x*AL9Ol+t^WlZ-3QW-*@kK3C2Y zy~|o_^2WcX?_NcgW1|!d0;XME@&q^`pM%K5euR>mv7WY@7t{O({pGQhj+Tyqs&3oX zi^Dzg`q8VUEY#4;$XI@^yIU_uo`p7=X4-4P8=v?Au)mU@XYMc{i`vhFhhF>>Q$7;9 zRmF|is$jk5N6OUdGZEG)vsWLkl72n_AJOVFAh3Nva zALs1tXwMFGG5I)YfqUZ#V_!7d?Or^8c;IDM7bm#W-Zp`q60c})2Q6Wt#g4|j zud^v)T>Uzm8Z#UxhZDL$f zBY*v?s)D9Sl_dmDE2VfhlJ$OfI+@l+`yH+0DEU_LJPPE*(qsnb`J5_xJ$w1evpvT{ z+#MQ+Bd*B9BT!(JVSmlVD;d}jkX$zVGqI$YHq4-+gO&k7E2|u2VAK&vGBV`Wy4{-nY+p_VdPa zFoE4L$G_%pMw~w7)Ij$;&K&sx&nbDOkWZidz2xD^ctb@#$A4nwt^PaF8Ktwqoa^aK z0)J00dIivqtPCx1H~nC~S|4>kzU0O8KW+*tnpI!+FYG}CpJ>0+2n&OkbBQJ2X}x9T zr()rgqs7I~Q~x#M>htpepJXjPnk;TJnO&B1PSKrY-Tb@qQ_0eoS@`NdYuC2qItl}U zfHS|lTebgwhS;zV3LROf?ND`iay%X)WL;VpNc``gf89SAOa1x2cEbqsd}}*SByHoz zACdSAlM9TG?B8E60Km3Nu_s&#=)PMz-%VZ3-=zEds?lpGG=Ko~>zt{+V1+@{CkX1euh;hgj~5M`+Gpo0?sWLS6_}!PO?8I ztZ(_hYTsz1M)(|20F4v-=F%VT^l^IcSA3rDe+1+4$T_2;!1(`qy^y$sNTif>LQ2!a z>>C>_Fw=z}tuN@*G02{M5%UWd8)jU4o=6Kv%5OUs&WDcgO5=jc=rOzfP&O(kzA=aN z7z{CV%6pmlDH=Kt=4W;DjacdD^I1yy{rz?6X=9I>Oo(fJA_%N&Ki8|YpCZe>YKUj_ z=m+M<=KC`t${`tYj-x*=>{ZyF<>w-?59ZNp%LaJC!F&gjynPOP&NqvPnk77j>%Nv-wg=g0U> zED!}${qc9$v6&vZu=;q`Yfu}f0$WgNz8QD*0dzr+$d3jmdIpBmt`iXRdpyog$LRY! zm|e|7lFhmf;y3%SKlonlg1ah-L4Me9?VgsLpzs@H)!hG6tCr`0NR{YyC3?jC6dSZK zLmG}RLK2s6ah(4r09)MmwrziE4a6_Fle1Oyb8BW$A%Kr($Y71;?0cbBAB`KI^*>%S zKPY}0`QLti#NY$S86A&j98TjnwedzUp0Rs|&RT0JCFk5)tF?+76>A2N18yJ3LFMGk zKyA0*gpG3o_I=;?{rP;l^gm0H1)86-VfF#?Zca}0(UKL|NAQ{jypJHzOUIs;V2Q;xVS~1RHrZSz6>8qDe}5YDM@Xjco02ec6C+$@$i>+z`P4w zFkEr$Ht2p|#OCttcEgDo%%re*vyB}o=6k{Oc#exj^#JL$H(#QM-CsSCE)Qz8E$Y4R zgl%FXfMAt>KOu0vYIh!{@2q0hRf0nD0N41C=>;2<9uP3))7;0vAA~j%2p?p_Mkxix z0jCdMIOTYreX z_r@?dlWrg+enC{Msh{nFaWG4kiwpTQ@!i{26Z?N1*L?2>nf19az{6kSkhYk=(!X$``7cEu)b4{yZRu-VFv4JktzRSlH!k{@;MKokjI92pH(+Z^ z^&s@H@fyE0-bX@JC-h*$<>ELFhFh4QVsf5HJOYA^{FKqq5+%GXE0T_!GNV;P+ zu3_uyD!$P9?OVKR@At0FJLYlYiGU{-1~;G+vjhyljeEEaqXNx)DAbPxRvGe zjY0U}{P9R(8b9Y{2_XDxO|TBCSPRuwvz)O^g*9N^`)%-LQC`NVx6Z(FY;Y|jHvqYR`F8o zYx*9?&bOpG-1dFXd1FK_-M@8AtMgd9bl0cRZT)daci$VyybmQzo#0IO{&BoqyS?6@ z8wSOJIT2Us)&^R3v@dw6$_q92B%QVMV1_dnYp)@7fpJS)#eC))8< zX}${r8!Ks9b(j@L|FE7_U=IncgB!2I@-hsT)uC>)^|2TpoWU#1t09o&Lcfmi)P(yg zuDqPde{z-z+FiweWD;HdM;$c594^$0nph}u_@YE`p>D?ZLHP%HASY#i(VXMMDb%)0DlVc4rz z+UT&X<^enASTSo)hUJ4Ue6|JYgVsWZRe?EsJF!rkAAdofm0x!>X*np16o|SluvtAY zW&NzQc9MD{l#tAJjqR3%vLPu4+-eWLyz3K4q(~a9;BmXY*!pJ$Z1ip=-THd=JE;?5pbIY#wqI%nTw1aRl^w zG0g8>n2$JP4~ukxel?#adDq+L8ivV$%AW^RO+m$Rs$Z%-MakXLWgFdU+qSZ`U8J9% zeqG?bSjC66^TpQgXT=j`CtbTvPubh4wI)w7aT98OpY7LdARJH#`-4<7barby1&@}< zQlt*)39POiFfee6X4Sa63wrv`NyJlmUfNYjUR`y*%@7|SA2>LHYIi!2pR~8L;PskQ zmIke3B@v1V=XU7V&H8&Xt`WbK5>$07ls|Acl-*PM%j`;`H=#k{Sy|B=HDNck(x%nSzLX^YRXu^-E8RR;x_qp$ zCEoNnh1t{={5K2=ZkW2zdj2imN_)^|!V8W5B=TzLxxr&}-{Ic9nM*PT))>`-dvHS%7o67$O00960 z>|NWkq#z6o&iwxu@8CW#_Gv>pBI?#w?dIVW!%>VOH)06)C%Cgq+U6S>ip^PHyQ8!p z1-J+`t&rERe|iSx1`;DUk0eo`9DsHWizMW(qip8@wjc4hTtW2s_rd+|u94;C-hv6s zW*hUx12WB*t&C+0z_sO#ODu4<&ACDAs`8CzD28GAuZn?&S|*uXm1we3Cy!I8gg>_C zIF@tBgy44M!*5WAfm7kRp+rORPyMyqiS^T1vV}qa+_OjdD8NRGmB7r(E2yjkPG*hq z)rw+@mAr$WZBM0SXH}2SdOn8E48si*8Y~&dqIq$7X4Z)|%tz0dI0 zN)$4pDd>=YfAqQsxr_@tzYi)uiT@1E$8&};UG?Fw2gDE zmP4VsLA>H{H@Y{)e=VZFLIW$5vajT4q*RS~drnt_{|~JLLSP&VNJe&Fq0j(7I65cv zN$j0^sqp%+pVtxWm3@SkhFReo+hi#^@QW}LhI|5%tW?zY^ZC^7;%_6Ob54gGPp1bC z*Izakiw~K>?4|<_r@Y7BN3!T$T2JGDP7#~K^R8GqD9<8ON>LOA--30Q@1B{O*vB3e zpXU`)9eEQY=-fQlIRU4}ar(!!{35``IYT3OI((qa3!Q(GjKgZ*nK>X(sgUYnuDngj z&g}p)IzGHN4r9Y{R}=I>M9b(9-ep;2No2iFfjeQFFiuPaZgi^qte#PRnnEeJ3#Xv$ z^6qd@g5j|J5ixPS!jm@@bK_|l>CbQgo}pZ@6F9;=%D-YQ(b`v>$*_HRm(zz9 z&7-$-Z(jTa;~T1z+gWNd$^u;{9J2oi^r8J=e=Gp zdE$-mKb3vI!rNDr8z&quv<(1}t6D4Sg}>kLd;@CuzK2(;o-4p7?qc2(`Y64c$7ft4 zf&6<7|K(+45BSyz6T=)eu5ZtRGv#QwCC1?sc`o2|^}Dl-<}U14<0o_Bm@~)oX;A*a z(>YkVf7r`!e`OAH&I86jM2*`jh1XZnj180)O81Fp?n4jq;`RPoabvB!$oL z|6i~+WWaK%{REwXy`!^?%e~+D&eA@3p^kfOVjdR!Wjor%DfUWfoopG|?;YVQa61PX zZp^1rd^k56>urlv%J}$aw)YZw0~jzVU04P^ibQAmT#b|9?{552HQU*h?XvH(<)-!N z#C*ZvHpp|$6P9ytngcQc_=!(DS65(AFR}Mx`}lU7^s>)?Fq3houph|$Ro?Ohl$*Xg zgd_KDHunwSxAZ>bL~!*kJRLp|$yNz|Twkg63tPY^{A3r$!^oeH|M=1__zeI6|Nq2Y z-FoaG2o&4q{U3O{o(mgx1V$w%-Ez?{%^HFVD7ZEsW;t4yq1bV6s*5?&qkj#CuG3hfK-Z$wz4Vn`gZ`HE}bJ!x%Kf$9U zOwN9CB`4xD(aFDdE^x=gbXmTPG2wmu)oWn!!aC~gy>|)f*U&H|04c9=;Mc#&_xf^} zr`O>^!zpj~XLG^yPy8W#g`RwjGlkumH}$7vpZ&CPyxvypSois;a+IZ#M*rcv!GPWKwlZX*K;z@2bO-$aKK0|6#f3d7qST*7CWd$L!vg)-O1pb?q-9Z#8xt9p#( zx#oSzw&UV>xC!S_k3J&6Y)d#!GS!pHm$ynpbn6b{$>Z_B4a_0lZVJPhNqSQ_;`-@j zbcuSNBB>c%#*4QH0bJAc>BmryG0BHj3ew9Mj9uwG{ibC7nfW2ppLGc#{CvKE%wJ$N zVWmHbsg=c%+fY4kN0{i^CIePkF;AJ?rR)jsPY&!bO^7OI zzwcM?k>fZD_!4}GCT93)oaa~ilg^>|#6E&b6CIv9Nt7YG%?l z_Mrf?D3-KnjdF6L)3w(voDmetTf7PpyTI2T;DF&$^~`JK2{a~tU=8)LHd zjs~ES7s>$}#4vD7A;k74$N%T$8R)s%%QkHjRx9F3Rx^SRzOFUaUi`dQ9iZRu8$3)w z-H5|XmIo@Ci@Olisa^As5{+750#maBvo?4g>gRE^uzyobdJMM)E8>(QX8TP{vKzeF zdc5rTl8pFST&(%`_60LfIKJfL@vpzwW~5nPm*;aNaz;-%ot^hW$d; ze?>xiFCwg8dDnbXs0Z5}1cVFi={50oGJ9&h(k`?9OT^!1dYI+&`(eJ!pzA*X00960 z1VA+oFV1!Z48jxLNJrDDyPl%sJ=15gf_1VGcXk z{38(4ky|1k;U1)#$cIykzZ&WdCk`;BT7!(LVpo(>dMtCQ;Z! zibRJXxj+t<@kBcZSC(ZdeTQDXV+3_Oqtsj{bNDUSN94g~qRTv70G8M~FwgVH(39&d zS-yRLKA#Y%vO6D8iswb#{$3LEh1+O30&lZigvvqHWq9EQY_+_f@-yoDrC<XkZc7Vw`=7+7au0{k-JrNXn1MkH;4; zvHaF6Vf^1FS$JOHw9`Sbj=_b?q!G#-hh~;a?-7sD5zj?JP&M0k=Xzal0%F@0pm6#7?KR-0YXe!w^`Q zVC-RXtR9DS((hrvZ>FXdYnuMe(0i{AS(hEF00!X3D0f-*#bS(6Kn`|Cz<%9;|9n2A z){YGHop|_NB-(%3NzZi32qj<829`bbcOqE&P}mEn1V4U$U%y#?+!{ZKa;*V>gv>be zrMRLd{|hq~u?tY1`2MWs=Xsuay(u4w5*K%sO5;zKQCV(6yKvL zi-XOdBeSw7(6pQNhxG~#L@$WLzbIb8h4++ijRPA^Oo8K%v!kkwMZdm{lCY-&M~OB# ztXTMjkMr^Eh4u(?dd*N-z~;Y0mfo}ESNKyse9rHjBG{-N+v`A$fCm=&erxKMmpvGV zyiX7}%$Gl&qwMdi32{hhfdBR)rPLZ{t6$G`ou-L1^=8`as~I@D4jv^|n^k>IzG+!@5^s9c<8MAN9~#wzqR<2H)|#OAYs7|Vji9GAC4!j zTEsgVDmz$i*Y(OckJ8)kA}by>q~xZ*!*juY+`y_sWVQ2qYJVM0<{|Cz zO8x-=0RR8QUE7x1APfX=_y2#ld#ULI%j_UAGV-R49&#K93n8SDzy#zD?n-#ZsB#hYEz3j->kAXQ)r~3Krm;4{(VS5ifs7SZ?fB zFZ`J%npM4>uprDNUY)~vo_sV_|BA$pUt4m&bIws~Rfm*jO_VjjNwCseqk%e4zN_BH zKQc{n06lXXO>%sWgP05XBd0`x$w5LtJrwA{^n1}(t?Xa=i>TMnSjF{b$vC^QUXLx_ z-oE@o@Q?A9!83+XUP@JlBb@mYGYo!~5UGMNc4-ghC*BdT3*Q%E_XXLC|NebcWDA7; z4isYiKk0;7ki?B-oc;LtsI~Tu+xwm6c&w01GF)Gd!G4XS{n|Kew?7!{l<$qu=#X(w zQ8wSm`Y-naf0j9{1Ad#xTVy_P)jEX(w!QOVCb+;{Kd-yuPUpq$!Ha=|{xe;)dN0Y~ zz$G8mULGTXPr&Z;^Rq+2>+37p^78WX_V$L3;|?m6<`oKyH{y6(<#m(H0uPt%ZSxX3 zj{9tiicfZ7bkMHl{o97&`Jg)xA8!O-);wPrhHW(o#Y}*EFaz*e`n8hBRAeCK=M?nI z5@XqN44@g@V!7_HJUFn0_;wkqGq4lK1r9XkE8{bd zdX!RBlfAnI_K7^ugsMP(ZU|Sp)N>_gWc?f>-tFL$#{_@hDEV9FQHx>1!$Z7Xi*aCi zq3i_XH3-&dMC=WQzfA2C_X9aoIH2%Wk>@-rj>>zwHy()vGyaP^c=1{3Ek$urBPG9j zP~G2Lh*_@ie!o z03jvMF*eaeU7eL$SfF}};}>KLdQ;)#qoVQ-KAZcGu|uu5w#v`5aRlYNxmL=fQgZT; zoB?N9IH1b_v`I3)B=sCG$n(U*OcVG}(e&(9zEtLU(d_(*nh(rU)*kwFjW+|S4Z^50 zL#wKHonH;}^Qlg%T}I%`@+#j}{kRi1)yRG6zqX8MPIKw2Z{rT1XZ^7aJ6<1y`q%gO z-zj;doJo98l+|Z8kIK%UV<8Lhxqrn8Gae_``9^YnL*<`ko=}3bC_Xy`=(u5om{qQ0 z^N2<>+r3P;H-5hsNMfRx5~`0BIA++wVS|)=Ic`<{0x&G|_!>9);<82iR7GCbH|A*bR$sAx z)<67%dDA6k-I^g`qmw!e|9CuDw*5|(mwb}#;{Q%CS}HubXFmpiz{~}$Y7k+dhzvmN__l|$k zX|!cTuJb%q#n~>tw=liA^>|qhEGPR#jFTt*<*9yUfX`XzuPBRe++?4h7$^vE!>}UE zu~VA&@c&s<#rR!~TkqL(apN7c!nw?|pnQKk^`G=<>w7420z5|9`kO_d(awK^umUWgdDGmtY~ZA%wm23GQ}mg`Ol@`_08S zpi=dK@FgkqRGN&h2YEjJaJ24ZQac0m;YKDJyHgAK`&1%T&Hyzpb)D^zi%5P#eYyE8itf8hUzF`qc9yRz zv!EPp6lg|Vvho>mUZ7$cD8j6q+q6{ z`Xx}M_bwG;`z)Oim=E>bm9hRs$vMcA;^2HZ_~Eb`KJTaWTJ|>o?!s0Du%JX3NbR4Bk*j>v4>J>gBayrH8-XxBd}}*F zNUzA(g~xInhacC~(d;Wonw9rd+^}bXliv(A#S7qxW}6npfsIf|w2t3j7-V?B*4l## zPYdaFM31T_WUsm~t~IczC_4}Ol|O~)SepndoKZZs(b`8E;o9c})vVXb`S!5V2XfdB zv5KjvDl}djvL+jNQs`{s=VofY*Ww&^7KU8n99CYkz{^0TVlJN}ziDY(_K)s$x0o!t zMXgn8H8}Da+JWN5^MI8TIhl#m*CHhGy2jrC8}h-W6aCu|LLsuZ5u$Kq7oy&VRIvn> z(fXRDnkycz<4T_A3JyA$T6kxrhEH&8ee8-Wk$;?`GoMQpnp(CqlU@6;fGz!m>v0Mm zWOjc+=4W$PoVGA!to!!LMfnWv_M{z~%|q^j6wZpfSM<1g@zaNn95uQ9NAXoV zGaH2@d3ngzuwQA+zp|>7D;GpXO+7cbNRNwX9d}WFhS%RCUEhz;6wlkk4b)J9d!gFa zW*!oRzq}q~c|PdLh$sY4}7yIJi2yf(oigTP!W7W4`!`hSWVb} zZB!zp{P;prGt6ft#ul+2h3yx{%fCer zmGY3yJZJVvByp^JJ*mfY+YiN$hnY0Ho$pR(4Yl<%-+(E*#4Xv6aHSulnhPo2eRF$V z9{6kR_G^az5>#*f{-Rrye&d7lZ#*w-H|n_pdIe!aIu$(vt`xHIr`vNRS8P9@@v{-s zx}LqB3af)nS_=Cx+G&ACFuK{=c}N+`GYl}yUAVKr z-PMNle!mewnWhPX=IK4drbgV$++)W{#@P_!@#N8O2s6x?G+&lwnkEj{h7hJ{n&+9d%8^sEgsb%Yv7)*s;mtTljl zQuR+ZpQmWmx`bnZ7{dw0mzSnBMr&0Y`>rm}S9#My*^($Vo=LnRA{$^hEtUZ~O`yp_3;IRvM%HA78cz=F6nhiFGGE|@tiU~D^%d&ikn;#lsub0uocJE#HVE)H|xu#F9NmZ zAC>zTkuKh+)dYE)9?lt{qY#4A38CsH%IL9<8u6Bl&R_Jrn1JWvY-+!Bc2SCYc5Pye ze$bv_y~B7$?IK*E20PQ9)z#+;SS2;|3qZ}=sC@H#FvJKjj%B5p_jObJUx@`3CwY2< z*g-9r1b4JCDsgP$AC4xCx{47;(vpI^0H^v?{i@m{+D~_=^0Tsibb0v{{?Un21l*$L z8t{I<(M#GZx*qNE8c3>cGWbX31{{A+>1PfMx|%;*^0R#qi|iP#4nCzPL#6clEpn+} z50>;mJi;iNrpdGqqR?LZiPf$fEIQLcORygOc${AWALIm~dcho3<1MRW+j!IqS)%YX zgMb6$z%#Z?_5pvtyd8zAy{h=3G0%aj+u1{b{>tn+{3>f7O||$UB)ByWVYv^KJ@B+t zS8`A7->k8RylfN~_^`pfY{0V(-=?SEKH>d%@CzO**=fUiJg5b?E-U}y-?R!!;3GdA zX@Rt^ZiMlyrpji?@;>&jx}oLi`BD56Yl%F5r<1Q(e5LLv^eEhdIs4&HC>-5)>iwVZ zUv{s+w2gwr(NU}T?d!FDxl#C!plGX?K+TVm>O+Ga{ke=+9OtJ~boYa-x6$vzSw<4X z?eWa)WqXYiMrdi%je{8T_M(=*6{QWB5vZRCOG>3r$7Z71xYx4^LU{GNUhXr9mCxP z3D&X{B<7F`&pB(NQ@WxPCQqjd?D}yXT7F`rNHHj?4xeYu>VUntOXn1PsiG^?aP(~Z zSRU|R&(A8ieLqP2VS+-tgVWja;c)N?dt_ksCl>hKdzs$7(+($FOg@+LGd3Mww_c2% zt90I!6Fre4n0#xfH7l`}3W-S08TI$Weh$9aUQQi8{T1MdE5`Ge~}aDGDO z#boW_&xuJIYS_>Ek+v`h9Wn}sI2mhQX)ye4 z=Fqcd5kL$s9mv13NEKhr+~e_Je8873EE9BohjtSul{(y3OKl-h{o{F_wE{1itpzEi z*Xt#t1>@K2W$*Ot1*y8UdL1*?d*7Zw${FfBxC8a{Xf|3ox!fQ%X7z@L7u{ zmCttem9blue`aT0dl1}ac*2^hp0Zle^8J4A`+iz@(aYyht|T`D`OrhYu%U8`!zVs^ z!SskR>FY3mOd9y0l1 z_Cn>{`47`HY2mxOA415jBKch z2srjLNA(LKV4e2)SQh7*%kgm|OiswWGaRFSWA%%q^BbA}Jj8)e`38I|y4=X;rL>VC z-ivNS%^pO)Kr)f}Wr<^TMQ|T(`P~ikcjGnP(1rC^42{|AyM8rmEDj1)fMXjMvjSG= z$mhu)l_9uC@Nk}gLLT6)RObipY5>z@cr5*=ajR>|SY9#OM;F2w-|Bbp^bQK?LM^~` zjmnkL3n0cnlq+gNrn{%v9S*}82X zLrn6k6X6>xVS=VRe<{>2Cpdb(w+%KlnjI$0AExp{8pC7OTgr)<)y(d4}`DEB-zaznoq!KhuRwpdz!pz!<{{37dz2U8klxeBMqy_fkj z2RJayljyu4GlM~~esay!LEW^e+&-Vr5P}|ha=g1TR`*XkfvtaV{2aR2_ zlbqNP*t$%=K?`PLj8{fZ?6^|9UY6?ykGpz}*5TN=A6;_l*CSZB)K6~J3K!e94d-il zu=N_^x8IBhLn*_&1oPViiR&3Fn0-{e07`%l%pYd_Wd7kPe!9jKl0DDx|89JNrl+BJT>j?*v&xhS{@+oHL5>}?%ldhj3ou~O;&u5-* zyu}}DNlUGnbMdKtDXy=|3EA*=;ODdZz5EnYt6=pvgs>4lGe3=?8`F!|GR|Mwb%aLF z>)C?ty>pEe6j+e++|Sk#JpFt<9qV_XN0TbUe)gLx8BN4z^@8!4#T^GYIqW~Q%f#Tb zm0{tlRA@}zuwLe#!58m?iD*hGYNzmOh@xs%j{_o~oz+&JV|?g8IuF(n{Nd-#JWmN> z+crhBWm!ITJ1m|e@^;sLG5SA*IXp2daaON!Dx#Rf5OdD#poCZpK;84UXSv|FGdfwm zn(>FqIgMxU`7{5qJ_qk*>n6eA+wcI>i*oX)Y682dKbK`$*A)}0Wg*Y!^XuztIA1J| zVE+7?l7$F_HX8iKnkkYR{xE(<^Oa7kzmBn|{J$G7@D5MD7$a^j;@IC{^JuT_#k#J; zc{%k%ygzd@UO;1Z#P@H=%jQcs#n=&Txi-dxV>#^TVPsQ3XLuj$Q_k*8Bf}Tlb7o-* zv&j$N^)ub`EWU#xOB=t3!tyO;$WIo(Z`=29UC#-y`FT&j;AvRLGVnZ`rkd@^@hGQ} zR$4;McMa*BXx?%>U(C+3yy*WGpB6HYj85*Co_Go!ZjAj^JmxMA>Sl-j0P}})W(PAp zH6LEuZzeD~$$1~@@7;OX=?BJ0JY|mZaX?4M=qC1!UU|q1-mG6_>bdCb?SQEp18}^?-M`bW0bCR9ix;|X)F9VhycWf``V94 zJ5P*TS><&$sS3Br!q>t$@T3ObJR=K$6PY18&SJVMQc(5(8sR2{3Ev(hrDuf$NX(GE z#Jf0g3YRK(=LLpL$$!LCz+-pGM01Z*H}5hH9eCKz+Jzr9*pX$Kq#eaz#Rm-?FtW)% zq&fx`HiR>QTUKWFQ0sk?pa&KsVWsp2-^Rg5d*KISM<0uX`r>r4I_^N2s69mpqnr<@ z{G1Im9(>1b5dp*+PBLDVdmUSIz}ZMHD5&Y2*BQle2BqioS=0P>T$1fn(r+>0Czrq3 zc@-qgH;L&e>oqdd8f@s~7MRH)0sE_9a<5cSa>;vBJW^iQQ9MY+B1BSTyW zY}SZZ+$o9`=;96@m6-jK!-D)45Gk>vL~^|kJY}zq01S2pn!^!tNl)9SguXEA1sgQM>l9BIe}z zi6XTn($&ub$%MIA+QeB#;=VfG<*#^kZLzTW-rnBWmj*qG^~^a~!~vxYr6~7et*!cS zp{~!?^6=wDUn`aWJaR!5yYlwer2F`GdAy``GzcA1jP4!OT9JJsZsNgQ z3r^1Wk@>7-UbXI69y|9r0)!?AT&b>1e!Wi{`9R=P?OWb_4bCqv0g)|>m;|cIex1?nj$>U;GW8OHJV-5>b~Cn2WcQc z%*T@PuH9;F9g!S|FP^X2QvOwGu>Rulynes0=EA%TDak(nZxw(a)bPuw!I`QT&?@M- zDD>4ju4*LJR|j+AC+``bXZidC00960B^wq>0=OhKdxH%uUy%6k*@92UuM@_1-hozTElYFq~eV9U*Xozp*m>MJnR zZYWStkLdHd1peG5RaD(;lh}%MAGZ8;t&7@B*Xj+}!IrE6D7;idXg0KwyV5kIBW3Gz zuNfDcuS!JevzHF&?KdW4 z9A2tuxge}R=>=y?f95ltLk@s`r0u2ukpOo)v7N4G9WgenR;je*Gi}~Ag;x!HP-&`* zZu)57NFkdGJmf;FnK`I}yP=dI!&%_k!z-^NC@T1>cISEQ$r>oEzY);D&B{h$ZvX@< z6>wyUH}P0|NC<0Apg|wXqwJuVEfV>n5y(5mPn4cZi526b z9LI{?ad&w3l+q!HU%|ZtEoF=`UT-%A8cDDIl;d#-qlZGXYWY1KN;}Fw=;+Bb{Fz;| z;qhk=2e5)AegDWJWmL2+t(0J^Kptp0JE3^yqpMj;#|6 z?t&mSei1On$-sJcizAT`LU)QYl;b|j=Y^sl`14u*az{y7?eC2I_Dd_C{$qzZp}EH6 zIDYb0?#cvvJL{nJSs_Qt!-ldOMyVA3k~cNC{2bGzkz9gp<2!}hP<~d(^E@wfbnHP# zsL$H{u*v^6n31}`PwGj&W%UTO3iLT@%YR`-jXlcB@3{AU)vKvnF}&KHm~XDxP!}7e z>kn7LLkVRvyz~RR@iPza@_DO2jq*nlA^T$6#q(38jcEX1Y=As`VJNoM((M$`TicY_$rWr$e0@M+xVA%H_+-V?Pt}Q#RJTHGd!eYmfdy|~ z009$fJbq-}tMu@=UeEGm%jYhlxKfu_14mZHY9p>unO|x`|}lAD!69QOXA^)SI%z`TR*n0X4{Lu<H?c(vY3#}+9aCOq3pZHY925(H{*bkbi@R6 z+E8vd%jJwqd%*pl;@Kl5jPq*nvPB;}0se&YQ0oz!+Xbub|NWIq#z6o?wtAmA70r9*XklkMX>Fim(9Z*#>NmJ zcO!nK?9#_HF*}z9K}L*)>*|(9+4D|S7pzB|+Z1oNQC~Zsqre}56W?sG)ec3TXyeNw zZvr31&%u~_ps3)I!zRU^C9v3+`ElLQs(5 zpu?RQ5dvmW?tF93r~u zxS2W~j*HH}>}TKsCkZ`My7Alt?DbK2b3eteWLWG^FpM= zAL^2KY5m;Db%y}*ilL{wacB#DrGWJeH#R~4c79t1J_*}mg`Y`HCF>H@dhR{>Hhmj| zZBYK8@^q*!`DAkF7qyH0bBPm*(GAqH_a~K{MmjI`d2FcuY)P5(Z%j6lVPxp-%o_AD z&~SPNE`iZHa0jD>a=Aa;>mC53p(dtw7p1F>LW6>(6&_IQ!QT_fi`)habAF}#1VZApdiA(&R=m6*xZ`bdaSTVkHD&ZlzyFv zo4V!K6*zWs^R46VHsnhaMS9y0jQUmq!dP$xyoG)yu6H`Ojt@wRd)KhyqeW54qn?@v3*6 zQuF^`o2m(O(4?o4EP25!uMa735rJ)4n}ztre`Z?#(+_#3KPR=|5!ZEnKC%iHdW|1$ z?jCprE#mkrGLb#~>V||N81)y4dicAq5qN(zS~Gloar_Ux>4k`F8t@g%-!AyKBZ?++ zRYvH?BYeWjJ#z;W8_|H}4?=-UT$yPg1~d`IWWVr9z?r5cy57juD%)V#&444|%M?x< zigv!r(Z25P39mP~9YUsRcolQhn3T^~I|#X)JPzDt&LI=mWt{pk`FuVDj%b2*)oIN4 zF>Bu67XtZ_+I1fIwvpLrzjX;C!u$OO?_rZ*S*BH(&Jo#Nz4FS-0sW7ObRf{vu1A=@ z>{y=MP5eUd8&`#6n)>D^j{j_*gt@MGUz3f8gIPshqc_WYD+%S`^}cM2pnRyJaFW>9 z3!@i?$#NbrAI!32SvWIIALGO9B+sefr+qeT8_4nM5@3FlZKsUCpuak8-hNoO;S8+4 zVxITnA`#5(GGiQ23t5PzHJ&&|&>)fnLSu+EoAk@@6=au5aDPw*fi670RK{c*|u}6~G zi^UL^Qry@~=uZ06=PQqgdldcmq<0X8%n~iDppg?aZYd-S88~pS=qqnlBuigQLNPhZx0v z9VDeaFiO>Vtm_G1a^P+j z77Fi-QHbVusp;!R`4IKjysixT`LtmTPU$b(gt=TY4=`|F^Vr2x*&%OrU9*vPw1#E$ z@&k|I=lcaz@2mb4VS5_(%eQK~9#rS?^?DiMtoZ$H$Cc)-Fx*$B8e^<1xZUr7<9aFa zZKRK8v0|f~X{5sf1cb`%0dQaA-{n)|`3G?TTrf6bk-Y7traROerPoswmNiRhSfQK@ zeA!aAn-RSQbMFS-4`OcQ!X@qeJPRCPSt;-m`ISF|d#*;ie}~Zex(!u5zhBDL)617F zyI217I3TZ*U*<_hLqCqKqq^C@AiGhv2EY_O2s-Asm(E`R00960%w17?N#L{p;eijfENP^U=a<87UzoxRIWEga^zRPWRsJn7*5D z1jkL-cRFC^30CI+$K=2Xi4?k|nC5Ul*%lt!^=ffd&r5_!v zr{&`ZHWqd(ysjCkex73J{TWGI*oc~33<*)@I&nYeQ@WfZTsPxn#5(P2Z`ZCAf8}?I z3jSNT^a^|f>fbYFhtF;F&OtjGGW4adLtVHLYj^L%u?Ae?Xf!|0CILQdGfwQ36i(ij ze@GS*P;L>%RIKneci&gLbo|=7A2r{~#}8DKHn=vZ;*7E(o$@|-=Q^c()ZOU?)%^0|oKF4axg zhae6^QBi$fI${`S3Hc`fHPQ94)2l{hUr8-Gpd)@{8q3ewvW-FRNfmMa;L-yWh`4=~ zj|q&;JGeVNNJW(Rz@NJ~99SfV&=yvFBMzMN#-;FmKEgQmZx>@jhht5nle&*Q%|Ucz z>sq?VpS>?yeoC>@ADBRQ?Nv7s+G;$PCQh6V%GQk@4B)VPh@-3Tb;zc$!(y<|G024xEqU=wa+ogSKT6d8EFD}o9t>SgTKkB*ZdAw^!NeGX>SJ&WmtCjI!dSCV9iu~M?O6`X z1pobbl+H_hmwzc@ZYwJbyHRPk-#<;#PO>ru&ah)n9$J=0tBwJUd_`bF6fk1w8NLcM#rRToelLxSHYf%?r>!nVgiMn0PcwG7GI^dU)j62dTEZU4Sb-w?GJmUT< zeu3>`(*7`Y*&<$>xb74nk6#^Bni=EI?5(c{pEQSSd@@e?2LJ&7|J+^McH=MzL~+ml z|NpkO_JMlN7z7rvn{=0lp2o2)gwQZTmwXt#O=bAod{cWTDj*XW%6p{nhTi+Ke3&^i zMJuzTPDTNen<{#LWFnkE&j#)3N!a~x(AUirmhsVlytg3p&rl4f^c3^y;-0I~qj+MO z_XjyX zGkgqwfxvZn&K}5_lR}p2&Q~=xa8mY;fM!B3>*+>|+hx+P5-Ih&NFrQ{7J~S-7kyuq zUMjb_Gxhlj<2GYO+h@gEyv`MI*1X)iAE*NE0YMS>6LR{XQval~2GUvcl6|TI%?-EH zqWJ__{it|l)ayMv8SZJt%s(%Xp<|Q;c#flzx(@t)aj0U^#4)@AM)*@)z}F~KCKSIS z+(4m*cga(DHNb%&N!O0c0Z)tIh=8pN>$+AKIQHWLcWl@*cJt80UDBWEoG=8|-$d}_ z5e9AW6LSohc|R;Kl>l851}igO;JVycu1_SL@`vGd@74FRDRl|s(q`2)f6K{DA==|Z zRVj-hXULC;W8%(C5vDj}My=h0M$NcQZ;i&?=w;~L%d%`=U*gilM&ThjH+BfE5%;6M zXAH6Fs7jV3=Fbrh;G__Eg37v|=xOR@<_KG3OKbhUJ85;~2r<3^Ld69!+LUBC%bwg1 z`>;zq*EA&o&|Sn;gk96$lSkTU+y{t2Xpkd`HUD(-jVt=MV^;SRLX+_$+EGSuq>uA_ z|4=vvaVU?T>3gcpTko_K?v;8Xl;`K4pP!m4!BIw&xH^g3It18g3sBp2l6{zii!*sA zuf~uER^M}4gamFgv;(6Vqdf-;*unTG?mX2eQt66xm)h;L#exw`zvVfq60}bGPgRn9 z`0)PicygNw5MI`G)&9ae7V`d>`%j`jJlPXhgCXV-+3}Vm=|(>wJw5RV;UhoT4k$)m^4BI6sT7vfSfwd&jyg)J@ zH>Kk?#2s9GLGqJ30y1n#bPjzc=wFdPFUu?PHp4D3_Me+hIA&mE2)gCv|Dw$IuVLKG zZ}vDJm6BjxDela~LDnR~fryW7y9kX>Tb$p!m(^UC$cCQAwXxL4@{z8LPp$dw2DkP%0Pw zAyKlqHB@>3dP^T;9`0|FMk050RR8oUE7x1 zAPfYD?En95&z_Rd2d!rZ31Prax~(31vMB)px{QQu-0Bm?-=g0AEF=bI1rGl7l&Mul zq|{L&w+!`~D$H@%zxM{~+wCUcsg#0Z_YTm9O1yCHcsY&B&6#M*4!3M& z052FSbrisphrzj6b&d5caGBBzwZ1mFyHlLC~V{bt;85rSEOjYF)$ zU%rsn0a~!UCrL?8ps?RFhE0MDV?9gvYzDXuE#=RgGr~?ddpRY&X2N51QWDM^H_tA0 z+RIAHLZLosZFK85?SBcuzq3hH=rEAk7Y$D8)w_8e(SPX>Kw)g>T@J3^gqInP{7_DhedD)wY z#tBT4x8kkgOmCe%JMb`9@=2T(_5yb`FIWb6UZi5np0&Za3#(Cxz)XxW=ZuZBiaAbP zs>Qg=Q%rT$^>;nTi_MX@k17FoxB~phM4&LIZ>O!jRf>bp?AiGz@~Of9q)4!@3=asyoGx3}Za|2ANco_13~eFILX`8hD0Y4Y#& z6Nw7Ff*lXw#rh3}UO&i(K%>Q={dVFVPd;<~U}QM*ciw*4W*+B#VmS}An+$8ErSP2Y z_dBme{c`2mdxiUTr1rGW&sE%bantL3?mt0;`t>?+ z;>8`%-$4#Leo%FS24M!b*t{|KHhy0DcZnTbFfd?bi@i3Fx-gDcV~WG(x)plZHBx>i z#`x>=bEMlr>9rf@<*#NFeK5QXx)y}QThFH{)LSFhU$?Ru7--x+c)T@x=uE`(@3Ur# zBTA-^kB?@1#%a)c`%U<^Clh-i_zeSv6YfcHpaLy#GfLwd(G=I>oN6tshKqClFTa0g zwMV``+)&PYFrtffB`BLOL@qo97YBB6Z$s~PbInx^7!d-t5qr&Pdj6=h6jO8+tYULf zC=aF>J>>kkzjKOt!#AyXp=to~&iB)NfqQ73%Xt2m$HgqzYb(ryEPbhyhyD5f9Ks9u z7!+M4R2R~$UgL%boczYti;0(agkeK43GF8TawNTNnC;~Yv@LIw6cqt8t4uBh+|)jC~2!s-GbEWAmb+~L}i~jTCtrh;B5uh!Rl;D$sYg! z0RR8AUE7l6CJYtEbWiP8em1Gfr*GC6LmsrKh0wv>sSQsaj~j!oM?&E6_n&`>=VpcE zKVe0`N^EHpU)7!!Ju<;7GGaDZDJ4PG)Q^SRw(ZByf5eDrTWq99E;f2qz-Ww8LBR04 zWXKq-TRD~jFH48`*(aq!eC=P91%87e=#6O_iPSnN{cq}IU*Sko1@|F@oHH|yl=5hW zG5t5Sb3pph-pD-vANZx6w-84H!m0n2oH+8f`GI9sbxxXYtp|;U??`^oNdpnlq5A#r zkHlIm7GKEjzL$5Ft7&fB=Q$Q|yvEa0`Fg&!f0BNtn<4BiTbqhqwFL4wXFAt$@2~!q z`(6_-tqn90{mdEh(YJeGUXoDIR$J5Aj0X`4jZ>ebNGjiox*|3S^oqrV4DQ7bc>`Dy*H@C07zvngzv}>n1=KM zJ&(qN=q4N3Xw)O>ym0L*&GhtaMHoNLssF`=8jioX3$)C8Q%ZWLa_EISi*bHRH*sDA zS+M*|!7*>Pi81c`4&|!j93v+c*oWGqZt0&Vj{M!GLI@h=ZSAK_+W>!&9Lil0C$2lE zeh57)f?PxTj~ki9p6Ehr=l2{XQ5^cuxW4dB!>Hl7c$*1o<_Th@3y^%NKhKM8q4wTcF$3Kg5@^%R1erTxU=2Pw6>ik?3Tj80w*UL%q2ZwJIai^Mx-Jd1)| zF$IE(uj~5u?He295JF0cJ#{Gs77Dv}ar_dH9k`44&AgCZLjEfPI^Wu{H}N5AsQ-xK zcXhJ9zF6Q%_kK#rI9BFk>fcZ{_UAlLSN?M;LQFvdj4N`^EEtc+BgR-t$vHpqhCtW; z?EB8+6vi1lV;badN-jNxN@l*K=E6R-lu}AT8yY{Qe?Wd#QG+IXOIR;vI=J_lkJ|FE zUjOm^J1dTSs^}Lryf`$d!f1;_fV~ww97en?1-@2~MgqS4T`4Y3L-rqylYsp}e*g61 zQ$&2id_gUt_;ifZpIUf0Xd)ZX6zaej*{WEF%O35 z1MMxg|IN#%LHYx}pNgKs*$_eldEhnfn&kxAdF+4P_o8@E|FHJms%(VaxUzt_2GI@S zu|yxx;!S@?O7aIg0Q%pz z7pX~Uea>ZmR|ugg3#OlMQ;ME1E|6!Cp&A?#f&8OPOm}@4aQ59S?{XzP`Q;T;YF}oLwAP$7M?DXP|ID zv)=LBT|T<}LVgy>TLAe7xTF>Q12y3K>6YesReVs|2J+9hj@7j54qplV+07wbD(l7C zGHAaxi#WFj;d*WJ(piW+r|mc*4G4i<>fdIO#~r}aW`_Y7^>?x z(Yg%uJK+98gj~IV@(Jur19qnN6ex}K(Rm?_w-K&D<3aNZUcHT;*Nj`t^PvN2H~)a} zp{wsp{e2aeYkl9g&3$i-FpuQw1k0=qRnG5&?r0kRr;)e7=0U?l|d zJ@|eN+GjvdTnFg4+jRuf^non;WQyB$E7K~#$#7i^;Nf&v*MZubbf&H@wts?tfn`H` zmJq`KeLj2x@sT`W=L_r!dOm`>JCQ8ZkK3&~e)qlRfm{Aw*JpKEX#B|Drj(jG@JLJn zzjwxyE%%cT3n+}hc?FVS_cvR2 zzMuLR00960%w5@%;~)&gp6ty3f9_UInSEgCX(Kh4u`_x|B@PH7gw&jVg}fs<<|WvW zgN@MWkKEh&G9-LS3E6_JsbRx6QeuFIjL=b~fpJq>)>q!``tigoLYn>;r1VhqPgGd} z0%lm(%pAEjsuaXqVL}dsYegC&_Lz7w!KbJN*kZ#|`aHx71d5EVs|_Reqo~=x*uW|q zDPVgRq79MK+d+VSnkw1rdcIDWXuy;GyjP$Ef6@`hc%IY0ZUcp!XzP&G(%ijzosu8G z_$0O#Ci!$DA|;O}(V!d6o%QR@HGm0K*R5z(+gWE-@h3}4RDM$RBRINXO}($U9$hUD zXKyE^w>h#L;I{*{V5OirKiQO=#hByZW;ycK$&XCm`anXYDLIAOEllkc5(oLx!GN6LVEjC>47&!glt`V_2Bwj=nox$iL@FzdK23- zWE%+XH+XX+Jq({29VGR#sP%V2D5*9Rr`#v2Dm9H0oDoEI*$mU&8*P*cCM;T>orIOP zz`<_c`M^vdH~8N1<9z`ac8G$Yir}@{A=0z&MXu=0Gq~C-Vxmo>E|f_Jz4~#iTY8M; z4X>)p6mr_K>>Hx~e_!P16@Ft4u#(gG#kSX4dL9sV+EtO>^+IYK;RYAQU;5{YOhbHb z)^KIKz~c^}0)+#4zZD@#epc+r?Qmp;br0F!gY> z>~KcGRS%8E*UZC%a9IBEsP3$KLPo zt+l>huX;Q;*FzQ5>k#Ue2B<0sj3`Rqx!BeTNe@tKtsl>iu%ogynmXC6Z?$|L_b+hw z5=fx2sF6c~O|AmNOpJ&a;&q3=!8}j%*Ck)3)mZtNRazLBsSNSQ*?e$)hEHfUy?+YJ zBd*E$>8s1Vb-Zilzs-rcHB{ll6;43_?D;*o>tu0P_qDL>4_%7n-EUn2}bjSy2xxh?$9%oHHm`^ z-`1}%p3Hc{U`3Wg+bgs)c<4^)`{#wneYwvL4Wb=$g;Hw6 zEat=}0m8f%-ro%}kQ}Ce$=h=SCz~rJfBY?Hw~I5Fe-<{ zprsp=(W?yFi^;egvp?Sku+LaassM~1eiEM35ZK#e4EXbwYEZA3-%0qDZ^|aLR@pFy zVkI<*QyA-%zsUTEgq(BT_*?vSIN4ohsyqnB(>7=Hj8+(`lf>Q3|C;`r*A8XQ(!O5k zWPj%ZcRlG=3;8$w%gQOFU+6{x>>6IFe9PYCzj7%5+2D9Z_T*_+p8t41b=f|69x?^* z9}lJKpD@o0OiT!vXa(x(6l%Zg7?*iH9*-AX+<~92ljCMo{`0utU&f(v3eIKJT1z?H zCppnv^E5nUUo;<=yqlYAV+Rs?6|W zHErU3ZB02g%6H;S4sZG?oX5u^C7;9UkMKrx3JnYA_1L&^KL0iDn6UfghV&XJ|0n*` z+e2xC0Hg_UG6HXgp(h;QGo<5n+ZeP}{T+Rf4s~j+Yd>pK`z*_c`JG&Em++q{J}?V5 zFT-w>Wl*W=pQ}q6+uLgokK3z4#@P~t_5@{%za*JR#9K~pIx=AVHm|}sbb+-oL0A#g zu34UddWFM)3@rb<`GaXG>09HLAB^SYxrKcDs;zjFKV10GtrFsBE-g%6GNcgBmxD|iho{(2m5zQt0pe2`|h7nKiVAn+S7 zUawV1;)@Ft9jJ`n|gG zv8w;!zMtLF)e0Q`7?yebhcubg3=4^VtvU5J^Fwk=NkT$3zB=JN5!Q5=(B$}5vJ)3# z*Ge?@@H`atjE183+w~@u#NjlOSDt94f{F7*Jq!=4_Ed3|;iRD? z#;1$p(ZoY*?U?bbEs*-TAJ*kw-kHh?(=Ap(qImW+5#s99j3f`4T8;&*zmhFf{oG%O z_IJtpqn-Ertw&KCc|0C{Py7SEZPU-$6!0e&5O}8$@q#1BaaGq-D*DH_%XnnEz-0QD z^_PA@7R~eEaE(WHG?f!$IU3Mf`}+FQXThB!?8sLpH2zCk>NvcpXPZZuRPCwO&-M8D zlyQbB<9yBI`J$Z{!t~(jb>#X$JLM^^-$bB{IBEao8p`A1S7! zU*P0JJ!UuDAY~00$1gDQ$fF>&z~bp>gaxc^2X54u)yr}6_Q)1ev(GswR%W-A8ZI|I@e<~Os|)q&oiu- z2b-slgDW@EXix|WMi;13^A7kyT{Uk1S`IiwMG<3z7nm>)ZSX=^z_E;*6q{QM#r(wM zOcmm)^*iI3oxvOWS)PFO;p2Q=rg_MsU>b><{hx-=b&MDU(17fnRW(V8UdFZ71{=9N z;^FOimoKm8pO}pC|0MN|d7ha36NMI@;Del_1nd9)^$$DIc^?ihRH|lkRqM_W?#Z^7q zycu2(=L;SQTl+cL*yn*b@crDukBAW7{Qykx1g|q5To=q468vZF=9OhcPJPTIx!Ueu zvu-@)7l8?EqTj&shK0E>o@IP$R`2yE8Ej*v>7z5<-7ZPI6`pB$77si6rbFO7!@Byc zU2%(7y}+2x3*()neu$;ZR@+-lp_)fcH%5E||*< z8#1rwu&!eA#&w)j?l5`FcAjtYRhIu$^_vaW5_x`wk0x31LI$!YRA|Z{>?qY`( ze`0n~^%T>8i}Bcmmj9wRy)?29zOx6o^PX=t}zbeuDjeN5TAxmx1XpQ>U^0bOGJM!)%iup?1`RZX1}_9&($p7 zQ=Y{{Vq?DZ4FCZD|I}UEa_b-r#GOw5|9@-j^FY04Eg>0U9GZQ|)TzT&R}kVmrL-Q& z8hInXqpj&I1^jN~>OgoJeF3Q@Ad<;wY+$PoZAM2?+K=Ymr8}dp>;k{v% z=MCT-#)%g6-*8n8&-FsN{H>RFIuHf6?2G#PPS+bnbbisuYBMyVbW+XKnBp05KLr;5&QkQl`C8EDc23E|USW#JbB-fP zM7Nd|V#x>dLesOcflpEI|6*%b5<39DvmuIagxTNW?-cty8FIsFr?wMj!OFz29tUM{ z)q`(;B4GN`^DV$Z8hlf#gWcF(RI1TGvZ{lA`uw|{z~6rzNXHmH;V)ZyXS(jReF*D@KjF{ED8PP-C%B7)YJ!&?9D z!3~U!ZfN00=fDZenXMr?(-gd3FSZ0A(;m8N{3bMC2<8=5gisUw_>lgxP>#kWS{V}* zCEy2ST-pgY`!&%270tHR-}s0l+ArR}sf@H7tG2V$@;%jmjOv&Bu;jl5bA96+K&Hu9 z*dfc|Zt#BxLQCCWGCLY6u|?#Jo)BpGV^sU2>$EuI&4l3}{1%dVUDuqme3Z-Nz}kD_uzZz%Y}VV3=X zYfdo&PsHyz<0+*2%S~9*Uj8U>$tP(xdjserFlt_+ev0-7qcHa|Q=M)F|6cO@EcpJH z);`hlwIeXg17g(rHAf#=_JIE@-F|IF9Od+kwzl_hlRRE1h?2xV00030|GZt@n&Th{ z)%4qW|L2`-buVn_5ja#b?s%1^29ciw8a3||+)F8PmRM`;4_Qd05*NI%skX3%0D+#5 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⩔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 UF+?~WZ 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

    8rW|*Bt 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%0Dc_u$+g$b=*Z>EDuQ&9cQRQfrS>R{lV!T>n+{B~ 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 -Ua|+z6UTJIdeuwN>X}WkCZ9lUg$ocEz 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 Y0Q%Mbh=G$*_{W8bSq27 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