# 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.models.model_builder.""" from absl.testing import parameterized import tensorflow as tf from google.protobuf import text_format from object_detection.builders import model_builder from object_detection.meta_architectures import faster_rcnn_meta_arch from object_detection.meta_architectures import rfcn_meta_arch from object_detection.meta_architectures import ssd_meta_arch from object_detection.models import ssd_resnet_v1_fpn_feature_extractor as ssd_resnet_v1_fpn from object_detection.protos import hyperparams_pb2 from object_detection.protos import losses_pb2 from object_detection.protos import model_pb2 class ModelBuilderTest(tf.test.TestCase, parameterized.TestCase): def create_model(self, model_config, is_training=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. Returns: DetectionModel based on the config. """ return model_builder.build(model_config, is_training=is_training) def create_default_ssd_model_proto(self): """Creates a DetectionModel proto with ssd model fields populated.""" model_text_proto = """ ssd { feature_extractor { type: 'ssd_inception_v2' conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } override_base_feature_extractor_hyperparams: true } box_coder { faster_rcnn_box_coder { } } matcher { argmax_matcher { } } similarity_calculator { iou_similarity { } } anchor_generator { ssd_anchor_generator { aspect_ratios: 1.0 } } image_resizer { fixed_shape_resizer { height: 320 width: 320 } } box_predictor { convolutional_box_predictor { conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } } } loss { classification_loss { weighted_softmax { } } localization_loss { weighted_smooth_l1 { } } } }""" model_proto = model_pb2.DetectionModel() text_format.Merge(model_text_proto, model_proto) return model_proto def create_default_faster_rcnn_model_proto(self): """Creates a DetectionModel proto with FasterRCNN model fields populated.""" model_text_proto = """ faster_rcnn { inplace_batchnorm_update: false num_classes: 3 image_resizer { keep_aspect_ratio_resizer { min_dimension: 600 max_dimension: 1024 } } feature_extractor { type: 'faster_rcnn_resnet101' } 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 { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } initial_crop_size: 14 maxpool_kernel_size: 2 maxpool_stride: 2 second_stage_box_predictor { mask_rcnn_box_predictor { conv_hyperparams { regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } fc_hyperparams { op: FC regularizer { l2_regularizer { } } initializer { truncated_normal_initializer { } } } } } second_stage_post_processing { batch_non_max_suppression { score_threshold: 0.01 iou_threshold: 0.6 max_detections_per_class: 100 max_total_detections: 300 } score_converter: SOFTMAX } }""" model_proto = model_pb2.DetectionModel() text_format.Merge(model_text_proto, model_proto) return model_proto def test_create_ssd_models_from_config(self): model_proto = self.create_default_ssd_model_proto() ssd_feature_extractor_map = {} ssd_feature_extractor_map.update( model_builder.SSD_FEATURE_EXTRACTOR_CLASS_MAP) ssd_feature_extractor_map.update( model_builder.SSD_KERAS_FEATURE_EXTRACTOR_CLASS_MAP) for extractor_type, extractor_class in ssd_feature_extractor_map.items(): model_proto.ssd.feature_extractor.type = extractor_type model = model_builder.build(model_proto, is_training=True) self.assertIsInstance(model, ssd_meta_arch.SSDMetaArch) self.assertIsInstance(model._feature_extractor, extractor_class) def test_create_ssd_fpn_model_from_config(self): model_proto = self.create_default_ssd_model_proto() model_proto.ssd.feature_extractor.type = 'ssd_resnet101_v1_fpn' model_proto.ssd.feature_extractor.fpn.min_level = 3 model_proto.ssd.feature_extractor.fpn.max_level = 7 model = model_builder.build(model_proto, is_training=True) self.assertIsInstance(model._feature_extractor, ssd_resnet_v1_fpn.SSDResnet101V1FpnFeatureExtractor) self.assertEqual(model._feature_extractor._fpn_min_level, 3) self.assertEqual(model._feature_extractor._fpn_max_level, 7) @parameterized.named_parameters( { 'testcase_name': 'mask_rcnn_with_matmul', 'use_matmul_crop_and_resize': False, 'enable_mask_prediction': True }, { 'testcase_name': 'mask_rcnn_without_matmul', 'use_matmul_crop_and_resize': True, 'enable_mask_prediction': True }, { 'testcase_name': 'faster_rcnn_with_matmul', 'use_matmul_crop_and_resize': False, 'enable_mask_prediction': False }, { 'testcase_name': 'faster_rcnn_without_matmul', 'use_matmul_crop_and_resize': True, 'enable_mask_prediction': False }, ) def test_create_faster_rcnn_models_from_config( self, use_matmul_crop_and_resize, enable_mask_prediction): model_proto = self.create_default_faster_rcnn_model_proto() faster_rcnn_config = model_proto.faster_rcnn faster_rcnn_config.use_matmul_crop_and_resize = use_matmul_crop_and_resize if enable_mask_prediction: faster_rcnn_config.second_stage_mask_prediction_loss_weight = 3.0 mask_predictor_config = ( faster_rcnn_config.second_stage_box_predictor.mask_rcnn_box_predictor) mask_predictor_config.predict_instance_masks = True for extractor_type, extractor_class in ( model_builder.FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP.items()): faster_rcnn_config.feature_extractor.type = extractor_type model = model_builder.build(model_proto, is_training=True) self.assertIsInstance(model, faster_rcnn_meta_arch.FasterRCNNMetaArch) self.assertIsInstance(model._feature_extractor, extractor_class) if enable_mask_prediction: self.assertAlmostEqual(model._second_stage_mask_loss_weight, 3.0) def test_create_faster_rcnn_model_from_config_with_example_miner(self): model_proto = self.create_default_faster_rcnn_model_proto() model_proto.faster_rcnn.hard_example_miner.num_hard_examples = 64 model = model_builder.build(model_proto, is_training=True) self.assertIsNotNone(model._hard_example_miner) def test_create_rfcn_model_from_config(self): model_proto = self.create_default_faster_rcnn_model_proto() rfcn_predictor_config = ( model_proto.faster_rcnn.second_stage_box_predictor.rfcn_box_predictor) rfcn_predictor_config.conv_hyperparams.op = hyperparams_pb2.Hyperparams.CONV for extractor_type, extractor_class in ( model_builder.FASTER_RCNN_FEATURE_EXTRACTOR_CLASS_MAP.items()): model_proto.faster_rcnn.feature_extractor.type = extractor_type model = model_builder.build(model_proto, is_training=True) self.assertIsInstance(model, rfcn_meta_arch.RFCNMetaArch) self.assertIsInstance(model._feature_extractor, extractor_class) def test_invalid_model_config_proto(self): model_proto = '' with self.assertRaisesRegexp( ValueError, 'model_config not of type model_pb2.DetectionModel.'): model_builder.build(model_proto, is_training=True) def test_unknown_meta_architecture(self): model_proto = model_pb2.DetectionModel() with self.assertRaisesRegexp(ValueError, 'Unknown meta architecture'): model_builder.build(model_proto, is_training=True) def test_unknown_ssd_feature_extractor(self): model_proto = self.create_default_ssd_model_proto() model_proto.ssd.feature_extractor.type = 'unknown_feature_extractor' with self.assertRaisesRegexp(ValueError, 'Unknown ssd feature_extractor'): model_builder.build(model_proto, is_training=True) def test_unknown_faster_rcnn_feature_extractor(self): model_proto = self.create_default_faster_rcnn_model_proto() model_proto.faster_rcnn.feature_extractor.type = 'unknown_feature_extractor' with self.assertRaisesRegexp(ValueError, 'Unknown Faster R-CNN feature_extractor'): model_builder.build(model_proto, is_training=True) def test_invalid_first_stage_nms_iou_threshold(self): model_proto = self.create_default_faster_rcnn_model_proto() model_proto.faster_rcnn.first_stage_nms_iou_threshold = 1.1 with self.assertRaisesRegexp(ValueError, r'iou_threshold not in \[0, 1\.0\]'): model_builder.build(model_proto, is_training=True) model_proto.faster_rcnn.first_stage_nms_iou_threshold = -0.1 with self.assertRaisesRegexp(ValueError, r'iou_threshold not in \[0, 1\.0\]'): model_builder.build(model_proto, is_training=True) def test_invalid_second_stage_batch_size(self): model_proto = self.create_default_faster_rcnn_model_proto() model_proto.faster_rcnn.first_stage_max_proposals = 1 model_proto.faster_rcnn.second_stage_batch_size = 2 with self.assertRaisesRegexp( ValueError, 'second_stage_batch_size should be no greater ' 'than first_stage_max_proposals.'): model_builder.build(model_proto, is_training=True) def test_invalid_faster_rcnn_batchnorm_update(self): model_proto = self.create_default_faster_rcnn_model_proto() model_proto.faster_rcnn.inplace_batchnorm_update = True with self.assertRaisesRegexp(ValueError, 'inplace batchnorm updates not supported'): model_builder.build(model_proto, is_training=True) if __name__ == '__main__': tf.test.main()